upd: Изменил диаграмму и код. Расширил пример и описание.
Почитать описание других паттернов.
Вернувшись домой, после непродолжительных посиделок у старого друга, я обнаружил, что оставил у него свой мобильный телефон, а вместе с тем и единственный в квартире будильник. Ситуация осложнялась тем, что завтра в 8:00 надо было быть на работе. Вариант вернуться за мобильником в 11 часов вечера я даже не рассматривал. И первое, что пришло мне на ум — написать свой будильник, причем с применением паттерна «Мост», который мне и без того надо было реализовать в рамках спецкурса. Как говорится, двух зайцев… Я думаю, не стоит пояснять что лег спать я под утро, но довольный собой. А утром, ровно в 7:00 меня победоносно разбудил мой bridge-будильник, весело наигрывая мотив из TBBT.
Как я до такого докатился, читайте под хаброкатом.
Отделить абстракцию от реализации так, чтобы и то и другое можно было изменять независимо. При использовании наследования реализация жестко привязывается к абстракции, что затрудняет независимую модификацию.
Невозможно представить объектно-ориентированное программирование и анализ без понятия абстракции и реализации. К тому же, современные языки программирования содержат специализированные конструкции для описания абстракций и реализаций. Любой адекватный разработчик, использует абстракции при решении тех или иных задач. Например java.util.List — абстракция списка, java.util.ArrayList — реализация данного списка на базе массивов, java.util.LinkedList — реализация на базе связных списков. При этом, единственный применяемый механизм, при описании абстракций — механизм наследования. Иными словами, реализации должны реализовывать (прошу прощения за тафтологию) интерфейс абстракции.
Однако, по мимо основного механизма описания абстракций — механизма наследования (реализации интерфейсов), существует и другой — применения паттерна «Мост».
Казалось бы, чем не устраивает механизм наследования? Почему был придуман этот паттерн? Все очень просто — коллективная разработка. Действительно, в больших проектах, это обыденное дело — программирование/проектирование абстракций и реализаций. Причем, порой бывает просто необходимым совершенно независимо развивать/модифицировать эти два, казалось бы связанных друг с другом понятия. При использовании механизма наследования это очень затруднительно. Любые изменения в интерфейсе абстракции тут же должны быть имплементированы в реализации.
Итак, отделяем абстракцию от реализации с помощью «Моста». Для этого, на все реализации заводим общий интерфейс, который они (реализации) будут имплементировать. В интерфейсе абстракции храним ссылку на интерфейс реализации. Теперь мы можем совершенно независимо модифицировать абстракцию — путем уточнения интерфейса абстракции и реализацию — путем имплементации интерфейса реализации. Единственное, на чем я хотел заострить внимание — проектирование интерфейсов абстракции и реализации. Как правило интерфейс реализации содержит простейшие методы, в то время как интерфейс абстракции методы более высокого уровня, реализация которых на самом деле является суперпозицией простейших методов из интерфейса реализации.
Реализуем bridge-будильник :) Пусть интерфейс будильника — AlarmClock, умеющий запускаться (start), останавливаться (stop) и собственно будить (toWake), интерфейс реализации — AlarmClockImpl, описывающий два метода — зазвонить(ring) и сообщить (notify). Очевидно, что метод toWake будет поочередно вызывать два этих метода из реализации. Сначала — сообщит, о том, что «время пришло», затем и зазвонит. Соединим данные интерфейсы мостом (ссылка на AlarmClockImpl внутри AlarmClock). Теперь напишем уточнение AlarmClock — как «зависающего будильника» (LockupAlarmClock) и две реализации AlarmClockImpl — как будильника играющего MP3 посредством выполнения внешней системной команды (ShellMP3AlarmClock) и будильника играющего MP3, посредством системных библиотек (SystemMP3AlarmClock).
Важно понимать, что при таком подходе, классического понятия «реализация абстракции» разглядеть сложно. Потому, что реализуется и абстракция и реализация абстракции и где искать виновных никто не знает. При этом, реализациями (в привычном смысле этого слова) будут являться всевозможные комбинации уточнений абстракций и реализаций интерфейсов реализаций (опять тафтология). В данном случае реализаций две — это зависающий будильник играющий MP3 через внешнюю команду и через системные библиотеки. Если мы напишем новое уточнение абстракции, скажем, в виде будильника демона, получим целых четыре реализации.
![](https://habr.com/images/px.gif#%3D%22http%3A%2F%2Fimg651.imageshack.us%2Fimg651%2F2825%2Fbridgeg.png%22)
Реализации SystemMP3AlarmClock, в приведенном ниже коде — нет. Я добавил данный класс в диаграмму, только лишь для лучшего понимания паттерна.
Продолжаем изучать паттерны :)
upd: Как многие тут подумали, моя цель была — написать будильник. Это не так. Я хотел лишь изучит соответствующий паттерн и применить его на практике. А забытый телефон лишь послужил толчком к написанию будильника.
upd2: Да я мог воспользоваться кроном, если бы мне действительно был нужен будильник.
upd3: Да я мог воспользоваться планировщиком виндовс.
Почитать описание других паттернов.
Предыстория
Вернувшись домой, после непродолжительных посиделок у старого друга, я обнаружил, что оставил у него свой мобильный телефон, а вместе с тем и единственный в квартире будильник. Ситуация осложнялась тем, что завтра в 8:00 надо было быть на работе. Вариант вернуться за мобильником в 11 часов вечера я даже не рассматривал. И первое, что пришло мне на ум — написать свой будильник, причем с применением паттерна «Мост», который мне и без того надо было реализовать в рамках спецкурса. Как говорится, двух зайцев… Я думаю, не стоит пояснять что лег спать я под утро, но довольный собой. А утром, ровно в 7:00 меня победоносно разбудил мой bridge-будильник, весело наигрывая мотив из TBBT.
Как я до такого докатился, читайте под хаброкатом.
Проблема
Отделить абстракцию от реализации так, чтобы и то и другое можно было изменять независимо. При использовании наследования реализация жестко привязывается к абстракции, что затрудняет независимую модификацию.
Описание
Невозможно представить объектно-ориентированное программирование и анализ без понятия абстракции и реализации. К тому же, современные языки программирования содержат специализированные конструкции для описания абстракций и реализаций. Любой адекватный разработчик, использует абстракции при решении тех или иных задач. Например java.util.List — абстракция списка, java.util.ArrayList — реализация данного списка на базе массивов, java.util.LinkedList — реализация на базе связных списков. При этом, единственный применяемый механизм, при описании абстракций — механизм наследования. Иными словами, реализации должны реализовывать (прошу прощения за тафтологию) интерфейс абстракции.
Однако, по мимо основного механизма описания абстракций — механизма наследования (реализации интерфейсов), существует и другой — применения паттерна «Мост».
Казалось бы, чем не устраивает механизм наследования? Почему был придуман этот паттерн? Все очень просто — коллективная разработка. Действительно, в больших проектах, это обыденное дело — программирование/проектирование абстракций и реализаций. Причем, порой бывает просто необходимым совершенно независимо развивать/модифицировать эти два, казалось бы связанных друг с другом понятия. При использовании механизма наследования это очень затруднительно. Любые изменения в интерфейсе абстракции тут же должны быть имплементированы в реализации.
Итак, отделяем абстракцию от реализации с помощью «Моста». Для этого, на все реализации заводим общий интерфейс, который они (реализации) будут имплементировать. В интерфейсе абстракции храним ссылку на интерфейс реализации. Теперь мы можем совершенно независимо модифицировать абстракцию — путем уточнения интерфейса абстракции и реализацию — путем имплементации интерфейса реализации. Единственное, на чем я хотел заострить внимание — проектирование интерфейсов абстракции и реализации. Как правило интерфейс реализации содержит простейшие методы, в то время как интерфейс абстракции методы более высокого уровня, реализация которых на самом деле является суперпозицией простейших методов из интерфейса реализации.
Практическая задача
Реализуем bridge-будильник :) Пусть интерфейс будильника — AlarmClock, умеющий запускаться (start), останавливаться (stop) и собственно будить (toWake), интерфейс реализации — AlarmClockImpl, описывающий два метода — зазвонить(ring) и сообщить (notify). Очевидно, что метод toWake будет поочередно вызывать два этих метода из реализации. Сначала — сообщит, о том, что «время пришло», затем и зазвонит. Соединим данные интерфейсы мостом (ссылка на AlarmClockImpl внутри AlarmClock). Теперь напишем уточнение AlarmClock — как «зависающего будильника» (LockupAlarmClock) и две реализации AlarmClockImpl — как будильника играющего MP3 посредством выполнения внешней системной команды (ShellMP3AlarmClock) и будильника играющего MP3, посредством системных библиотек (SystemMP3AlarmClock).
Диаграмма классов
Важно понимать, что при таком подходе, классического понятия «реализация абстракции» разглядеть сложно. Потому, что реализуется и абстракция и реализация абстракции и где искать виновных никто не знает. При этом, реализациями (в привычном смысле этого слова) будут являться всевозможные комбинации уточнений абстракций и реализаций интерфейсов реализаций (опять тафтология). В данном случае реализаций две — это зависающий будильник играющий MP3 через внешнюю команду и через системные библиотеки. Если мы напишем новое уточнение абстракции, скажем, в виде будильника демона, получим целых четыре реализации.
![](https://habr.com/images/px.gif#%3D%22http%3A%2F%2Fimg651.imageshack.us%2Fimg651%2F2825%2Fbridgeg.png%22)
Реализация
Реализации SystemMP3AlarmClock, в приведенном ниже коде — нет. Я добавил данный класс в диаграмму, только лишь для лучшего понимания паттерна.
// Интерфайс абстракции
class AlarmClock {
private:
virtual void toWake() = 0;
protected:
/**
It`s our bridge to implementation
*/
AlarmClockImpl *bridge;
public:
virtual void start() = 0;
virtual void stop() = 0;
};
// Интерфейс реализации
class AlarmClockImpl {
public:
virtual void ring() = 0;
virtual void notify() = 0;
};
// Уточнение абстракции будильника в качестве зависающего
class LockupAlarmClock : public AlarmClock {
private:
WORD hourAlarm; // час расплаты
WORD minutesAlarm; // минута расплаты
bool waitForWake; // флаг признака ожидания
virtual void toWake();
protected:
public:
LockupAlarmClock(AlarmClockImpl& bridgeImpl, WORD hour, WORD minutes);
virtual void start();
virtual void stop();
};
LockupAlarmClock::LockupAlarmClock(AlarmClockImpl& bridgeImpl, WORD hour, WORD minutes) {
this->bridge = &bridgeImpl;
this->waitForWake = false;
this->hourAlarm = hour;
this->minutesAlarm = minutes;
}
// Вставай!!! Вставай!!
void LockupAlarmClock::toWake() {
this->bridge->notify();
this->bridge->ring();
}
// Запускаем процесс "зависания" будильника
void LockupAlarmClock::start() {
// start lockup process
SYSTEMTIME time;
waitForWake = true;
while (waitForWake) {
GetLocalTime(&time);
if (time.wHour == this->hourAlarm && time.wMinute == this->minutesAlarm) {
waitForWake = false;
}
Sleep(100);
}
toWake();
}
void LockupAlarmClock::stop() {
// stop lockup process
waitForWake = false;
}
// Уточнение интерфейса реализации в качестве будильника, играющего MP3 через внешнюю команду
class ShellMP3AlarmClock : public AlarmClockImpl {
private:
string cmdplay; // сама команда
protected:
public:
ShellMP3AlarmClock(const string& cmd);
~ShellMP3AlarmClock();
virtual void ring();
virtual void notify();
};
ShellMP3AlarmClock::ShellMP3AlarmClock(const string& cmd) {
this->cmdplay = cmd;
}
void ShellMP3AlarmClock::ring() {
// run command
system(cmdplay.c_str());
}
void ShellMP3AlarmClock::notify() {
cout << "ALARMING!" << endl;
}
* This source code was highlighted with Source Code Highlighter.
Продолжаем изучать паттерны :)
upd: Как многие тут подумали, моя цель была — написать будильник. Это не так. Я хотел лишь изучит соответствующий паттерн и применить его на практике. А забытый телефон лишь послужил толчком к написанию будильника.
upd2: Да я мог воспользоваться кроном, если бы мне действительно был нужен будильник.
upd3: Да я мог воспользоваться планировщиком виндовс.