他のパターンの説明を読んでください 。
背景
帰国後、古い友人との短い集まりの後、私は携帯電話を彼と一緒に置き、同時にアパートで唯一の目覚まし時計を置いていたことがわかりました。 明日の午前8時に仕事をする必要があったため、状況は複雑でした。 夕方11時に携帯電話に戻るという選択肢すら考えていませんでした。 そして、私が最初に思いついたのは、特別なコースの一部として既に実装しなければならなかった「ブリッジ」パターンを使用して、目覚まし時計を書くことでした。 彼らが言うように、1石で2羽の鳥...私は朝寝たと説明する価値はないと思いますが、私は自分自身に満足しています。 そして、朝のちょうど7:00に、橋の目覚まし時計が意気揚々と目を覚まし、TBBTの動機を元気よく演じました。
私がこれに来たとき、Habrokatの下で読んでください。
問題
両方を独立して変更できるように、抽象化を実装から分離します。 継承を使用する場合、実装は抽象化と密接に結びついているため、独立した変更が困難です。
説明
抽象化と実装の概念なしにオブジェクト指向のプログラミングと分析を想像することは不可能です。 さらに、最新のプログラミング言語には、抽象化と実装を記述するための特殊な構造が含まれています。 適切な開発者は、特定の問題を解決するために抽象化を使用します。 たとえば、java.util.Listはリストの抽象化、java.util.ArrayListは配列に基づくこのリストの実装、java.util.LinkedListはリンクリストに基づく実装です。 さらに、抽象化を記述するときに使用される唯一のメカニズムは、継承メカニズムです。 言い換えると、実装は抽象化インターフェースを実装する必要があります(タフトロジーについて謝罪します)。
ただし、抽象化を記述するための主要なメカニズム-継承のメカニズム(インターフェイスの実装)に加えて、もう1つ-「ブリッジ」パターンのアプリケーションがあります。
何が継承のメカニズムに合わないように思われますか? なぜこのパターンが発明されたのですか? すべてが非常に簡単です-集合開発。 実際、大規模なプロジェクトでは、これは一般的なことです-抽象化と実装のプログラミング/設計。 さらに、これらの2つの一見関連する概念を完全に独立して開発/修正する必要がある場合もあります。 継承メカニズムの使用は非常に困難です。 抽象化インターフェースへの変更は、すぐに実装する必要があります。
そのため、「ブリッジ」を使用して、抽象化と実装を分離します。 これを行うために、すべての実装(実装)が実装する共通インターフェースを設定します。 抽象化インターフェースで、実装インターフェースへのリンクを保存します。 抽象化と実装インターフェースを改良することにより、実装インターフェースを実装することにより、完全に独立して抽象化を変更できます。 私が注目したいのは、抽象化と実装のインターフェースの設計だけでした。 原則として、実装インターフェースには最も単純なメソッドが含まれますが、抽象化インターフェースには高レベルのメソッドが含まれ、その実装は実際には実装インターフェースからの最も単純なメソッドの重ね合わせです。
実用的なタスク
ブリッジアラームを実装します。 明らかに、toWakeメソッドは、これら2つのメソッドを実装から交互に呼び出します。 最初は「時が来た」ことを知らせ、それから鳴ります。 これらのインターフェイスをブリッジで接続します(AlarmClock内のAlarmClockImplへのリンク)。 次に、AlarmClock仕様を「ハンギングアラーム」(LockupAlarmClock)として記述し、AlarmClockImplの2つの実装を、外部システムコマンド(ShellMP3AlarmClock)を実行してMP3を再生する目覚まし時計とシステムライブラリ(SystemMP3AlarmClock)を使用してMP3を再生します
クラス図
このアプローチでは、「抽象化の実装」の古典的な概念を見分けるのが難しいことを理解することが重要です。 抽象化と抽象化の実装の両方が実現されており、どこに罪悪感があるのか誰も知らないからです。 さらに、(通常の意味での)実装は、抽象化の洗練と実装インターフェースの実装(すべて、タフトロジー)のあらゆる種類の組み合わせになります。 この場合、2つの実装があります。これは、外部コマンドとシステムライブラリを介してMP3を再生するフリーズアラームクロックです。 たとえば、悪魔の目覚まし時計の形で、抽象化の新しい改良版を書くと、4つの認識が得られます。
実装
以下のコードには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:はい、Windowsスケジューラを使用できます。