デザインパターン列ディレクトリ
創造的デザイン パターン - シングルトン パターン/ファクトリー パターン/抽象ファクトリー
動作デザイン パターン: テンプレート デザイン パターン/オブザーバー デザイン パターン/戦略デザイン パターン
構造デザイン パターン: デコレーター
モード
デザインパターンの分類
デザインパターンは、創造的デザインパターン、構造的デザインパターン、動作的デザインパターンの3種類に分類できます。
作成デザイン パターン: これらのパターンには、単純なファクトリ パターン、ファクトリ メソッド パターン、抽象ファクトリ パターン、シングルトン パターン、ビルダー パターン、プロトタイプ パターンなどのオブジェクトの作成メカニズムが含まれます。
構造設計パターン: これらのパターンには、アダプター パターン、ブリッジ パターン、構成パターン、デコレーター パターン、外観パターン、フライウェイト パターン、プロキシ パターンなどのクラスとオブジェクトの組み合わせが含まれます。
動作設計パターン: これらのパターンには、責任連鎖パターン、コマンド パターン、インタプリタ パターン、イテレータ パターン、中間パターン、メモ パターン、オブザーバ パターン、状態パターン、戦略パターン、テンプレート メソッドパターン、訪問者パターンなど、オブジェクト間の通信と対話が含まれます。
この記事は、構造デザインパターンにおけるデコレーターと組み合わせデザインパターンについてまとめたものです。各設計パターンの定義は比較的あいまいですが、コードを直接見て理解できます。
デザインパターンの設計原則
依存関係の逆転: 高レベルのモジュールは低レベルのモジュールに依存すべきではなく、両方とも抽象化に依存する必要があります。抽象化は具体的な実装に依存すべきではなく、具体的な実装は抽象化に依存する必要があります (抽象化に依存することを忘れないでください)。
オープンとクローズ: クラスは拡張 (結合と継承) に対してオープンであり、変更に対してクローズである必要があります。
インターフェイス指向: 変数の型は特定の具象クラスとして宣言されず、インターフェイスとして宣言されます。
クライアント プログラムはその必要はありません。オブジェクトの型の詳細を知っていれば、オブジェクトのインターフェイスだけを知っていればよい;
システムの各部分の依存関係を減らし、「高い凝集性と疎結合」の型設計スキームを実現する; (次のことだけを覚えておいてください)インターフェイスを公開し、インターフェイスを呼び出すだけです)。
変更点をカプセル化する: 安定点を変更点から分離し、変更点を拡張および変更する; 安定点と変更点の実装レベルを分離する; 単一の責任 : クラスは変更の理由を 1 つだけ持つ必要があります(つまり、つまり、変更点が多すぎてはいけません)。Liskov 置換: サブタイプは親クラスを置き換えることができなければなりません。これは主に、サブクラスが親クラスの実装をオーバーライドするときに発生し、元々親タイプを使用していたプログラムにエラーがある可能性があります。親クラスのメソッドはカバーされていますが、親クラスのメソッドの責任は実装されていません(つまり、サブクラスは親クラスのメソッドをオーバーライドできますが、親クラスの必要な機能は保証されなければなりません)。インターフェイスの分離: クライアントは、使用しないメソッドに依存することを強制されるべきではありません。これは通常、より多くのインターフェイスを持つクラスを処理するために使用され、これらのインターフェイスには多くの責任が伴います。クライアントは、必要のないインターフェイスに依存すべきではありません。クラスの別のクラスへの依存関係は、最小のインターフェイスに基づく必要があります。継承よりも構成
:継承結合度が高く、合成結合度が低い。
デコレータパターン
追加の責任をオブジェクトに動的に追加します。機能を追加するという点では、装飾はサブクラス化よりも柔軟です。
この設計パターンを説明するには、料理 (食品やさまざまな調味料を含む) のコストを計算する例を使用します。
レストランでは、麺類やさまざまな調味料などの食費を計算する必要があります。
#include <iostream>
#include <string>
using namespace std;
// 食品类
class Food {
protected:
string des;
double price;
public:
virtual double cost() = 0;
string getDes() {
return des;
}
void setDes(string des) {
this->des = des;
}
double getPrice() {
return price;
}
void setPrice(double price) {
this->price = price;
}
};
// 面条类
class Noodles : public Food {
public:
double cost() override {
return getPrice();
}
};
// 中式面条类
class ChineseNoodles : public Noodles {
public:
ChineseNoodles() {
setDes("中式面条");
setPrice(25.00);
}
};
// 装饰器类
class Decorator : public Food {
protected:
Food* desFood;
public:
Decorator(Food* desFood) {
this->desFood = desFood;
}
double cost() override {
cout << desFood->getDes() << "价格:" << desFood->getPrice() << " 配料如下:"
<< getDes() << " 价格:" << getPrice() << " 总价" << (getPrice() + desFood->cost()) << endl;
return getPrice() + desFood->cost();
}
};
// 孜然类
class Cumin : public Decorator {
public:
Cumin(Food* desFood) : Decorator(desFood) {
setDes("孜然");
setPrice(2.00);
}
};
// 胡椒类
class Peper : public Decorator {
public:
Peper(Food* desFood) : Decorator(desFood) {
setDes("胡椒");
setPrice(3.00);
}
};
int main() {
// 先定义一个被装饰者,返回对象要为最顶层的对象,这样被装饰者才能接受
Food* noodles = new ChineseNoodles();
// 定义一个装饰者对象
Food* cumin = new Cumin(noodles);
// 输出为:中式面条价格:25配料如下:孜然价格:2总价27
cout << "-----------面条+孜然------------------------" << endl;
cumin->cost();
cout << "-----------面条+胡椒------------------------" << endl;
Food* peper = new Peper(noodles);
peper->cost();
cout << "-----------面条+胡椒+孜然------------------------" << endl;
peper = new Peper(cumin);
cout << "面条+胡椒+孜然价格:" <<peper->cost();
delete cumin;
delete noodles;
delete peper;
return 0;
}
「ヌードル + ペッパー + クミン」の例では、デコレータ クラスのコストの出力がネストされているため、ログの出力が乱雑になります。
構造:
- 装飾された抽象インターフェイス (Food): 基本的な関数メソッドと装飾メソッド インターフェイスを提供します。
- 特定のデコレータ (ヌードル): 抽象クラスを継承し、メソッド インターフェイスを実装します。
- Decorator パブリック クラス (Decorator): 統一された装飾メソッドを実装します。
- 具体的なデコレータ クラス: カスタム デコレータ プロパティ。
使用シナリオ: ソフトウェア開発プロセスでは、既存のコンポーネント (定義されたオブジェクト)を使用したい場合があります。これらのコンポーネントは、一部のコア機能のみを実行できます。ただし、その機能は、アーキテクチャを変更せずに動的に拡張できます。したがって、これらはすべてデコレータ パターンを使用して実装できます。
特徴:
デコレータのデザインパターンは合成+継承の形で実装されます。
装飾クラスと装飾クラスは互いに結合せずに独立して開発できます。
デコレータ パターンは継承の代替手段であり、実装クラスの機能を動的に拡張できます。