C++デザインパターン(23種類)のまとめとコード実装

目次

デザインパターンの 7 つの原則:

開閉原理:

単一責任の原則:

リヒター置換原理:

依存関係逆転の原則:

インターフェイス分離の原理:

ディミット原則 (あまり知られていない原則):

合成再利用の原則:

3 つの主要なモードとその特徴:

作成モード:

構造パターン:

行動パターン:

--------------------私は区切り、次はクリエイティブモードです------------------ ----

通訳モード:

アドバンテージ:

欠点:

該当するシーン:

実装コード:

ビルダーモード:

欠点:

アドバンテージ:

工場出荷時モードとの比較:

該当するシーン:

実装コード:

単純な工場パターン:

アドバンテージ:

欠点:

工場出荷時の方法:

実装コード:

プロトタイプモード:

アドバンテージ:

欠点:

該当するシーン:

実装コード:

シングルトンモード:

アドバンテージ:

欠点:

ステップ:

詳細:

実装コード:

-------------------私が境界線、次は構造パターン ------------------ - ----

アダプターモード:

アドバンテージ:

欠点:

該当するシーン:

予防:

実装コード:

ブリッジモード:

アドバンテージ:

欠点:

該当するシーン:

実装コード:

結合エンティティ モード:

アドバンテージ:

欠点:

該当するシーン:

実装コード:

デコレータパターン:

アドバンテージ:

欠点:

該当するシーン:

実装コード:

外観モード(ファサードモード):

アドバンテージ:

欠点:

該当するシーン:

実装コード:

フライウェイトモード:

アドバンテージ:

欠点:

該当するシーン:

実装コード:

エージェントモード:

アドバンテージ:

欠点:

該当するシーン:

実装コード:

------------------------私は境界線です、以下は行動パターンです----------- -- ------

責任連鎖モデル:

アドバンテージ:

欠点:

該当するシーン:

実装コード:

コマンドモード:

アドバンテージ:

欠点:

該当するシーン:

実装コード:

通訳モード:

アドバンテージ:

欠点:

該当するシーン:

実装コード:

イテレータパターン:

アドバンテージ:

欠点:

該当するシーン:

実装コード:

メディエーターモデル:

アドバンテージ:

欠点:

該当するシーン:

コード:

メモモード:

アドバンテージ:

欠点:

該当するシーン:

実装コード:

オブザーバーモード:

アドバンテージ:

欠点:

該当するシーン:

コード:

ステータスモード:

アドバンテージ:

欠点:

該当するシーン:  

コード:

戦略モード:

アドバンテージ:

欠点:

該当するシーン:

予防:

コード:

テンプレートモード:

アドバンテージ:

欠点:

該当するシーン:

コード:

ビジターモード:

アドバンテージ:

欠点:

該当するシーン:

コード:

参考:


デザインパターンの 7 つの原則:

開閉原理:

        拡張の場合は開き、変更の場合は閉じます。プログラムを拡張する必要がある場合、元のコードを変更することはできませんが、元のコードを拡張してホットスワップ可能な効果を実現することはできます。

単一責任の原則:

        クラス変更の理由は 1 つだけであってはならず、つまり、各クラスが 1 つの責任を実装する必要があり、そうでない場合はクラスを分割する必要があります。


リヒター置換原理:

        これはオブジェクト指向設計の基本原則の 1 つです。基本クラスが出現できる場所であればどこでも、必ずサブクラスを出現できます。これは継承と再利用の基礎であり、派生クラスが基本クラスを置き換えることができ、ソフトウェア ユニットの機能が影響を受けない場合にのみ、基本クラスを真に再利用でき、派生クラスもそれに基づいて新しい動作を追加できます。蓄積の。これは「開閉原則」の補足です。開閉原理を実現するための重要なステップは抽象化であり、基底クラスとサブクラス間の継承関係は抽象化の具体的な実装であるため、リスコフ置換原理は抽象化を達成するための特定のステップの仕様です。


依存関係逆転の原則:

        これはオープニングとクロージングの原則の基礎であり、具体的な内容は次のとおりです: インターフェイス指向プログラミングは具体性ではなく抽象化に依存します。コードを記述するときに具象クラスを使用すると、具象クラスとは対話せず、具象クラスの上位レベルのインターフェイスと対話します。


インターフェイス分離の原理:

        各インターフェースには、サブクラスによって使用されないが実装する必要があるメソッドはありません。そうでない場合は、インターフェースを分割する必要があります。複数の分離されたインターフェイスを使用することは、単一のインターフェイスを使用するよりも優れています。


ディミット原則 (あまり知られていない原則):

        クラスが依存するクラスについての知識が少なければ少ないほど良いのです。依存クラスがどれほど複雑であっても、ロジックはメソッド内にカプセル化され、パブリック メソッドを通じて外部に提供される必要があります。このようにして、依存クラスが変更されたとき、クラスへの影響を最小限に抑えることができます。


合成再利用の原則:

        継承の代わりに合成/集計を使用してみてください。

3 つの主要なモードとその特徴:

作成モード:

        インスタンス化プロセスを抽象化します。これらは、システムがそのオブジェクトがどのように作成、構成、表現されるかに依存しないようにするのに役立ちます。

(抽象ファクトリパターン、ビルダーパターン、ファクトリパターン、プロトタイプパターン、シングルトンパターン)

構造パターン:

        クラスとオブジェクトを組み合わせてより大きな構造を取得する方法が含まれます。作成パターンはクラスまたはオブジェクトのインスタンス化に焦点を当て、構造パターンはオブジェクトをより柔軟に構築するために、複数のクラスまたはオブジェクトを組み合わせてより複雑なオブジェクトにすることに焦点を当てます。

(アダプターモード、ブリッジモード、複合エンティティモード、デコレータモード、アピアランスモード、フライウェイトモード、プロキシモード)

行動パターン:

        これには、アルゴリズムとオブジェクト間の責任の分散が含まれます。オブジェクトとクラスのパターンだけでなく、それらの間の通信パターンについても説明します。継承メカニズムを使用して、クラス間で動作をディスパッチします。

(責任連鎖パターン、コマンドパターン、インタプリタパターン、イテレータパターン、メディエータパターン、メメントパターン、オブザーバパターン、状態パターン、戦略パターン、テンプレートパターン、ビジターパターン)

--------------------私は区切り、次はクリエイティブモードです------------------ ----

通訳モード:

与えられた言語 (式) を指し、その文法の表現を定義し、インタプリタを使用してその言語の文 (式) を解釈するインタプリタを定義します。

アドバンテージ:

    1.拡張性に優れています。文法変換はクラスを通じて実装され、クラスを拡張すると解釈機能を拡張できます。

    2. 実装難易度が低い 構文ツリー内の各式ノードクラスは一定の類似性を持っているため、比較的実装が容易です。

欠点:

    1. 実行効率が低い。通常、インタプリタ内にはループや再帰文が多数存在し、解釈される文が複雑な場合、プログラムのパフォーマンスに大きな影響を与えます。

    2. クラス拡張の問題。ルールが増えるとクラスの数も増えます。

該当するシーン:

    特定の種類の問題が頻繁に発生する場合、たとえば、簡単な構文の説明、コンパイラ、演算式の計算、正規表現、ログ処理: スクリプト言語やプログラミング言語を使用してログを処理する場合、

    大量のレポートが生成されるため、ログを解析してレポートを生成する必要があります。

    各サービスのログ形式は異なりますが、データの要素は同じです。この場合、プログラムを通じて上記の問題を解決する主な解決策は、インタープリター モードを使用することです。インタープリター モードは、日常のプロジェクトではほとんど使用されません。

アダプター パターンと比較します。 2 つのパターンは似ていますが、アダプター パターンではアダプターのルールについての事前の知識は必要ありません。インタプリタモードでは、事前にルールを記述し、そのルールに従って通訳を行う必要があります。

実装コード:

#include<iostream>
#include<list>
#include<vector>
#include<algorithm>

class Context{
public:
    Context(int num){
        m_num=num;
    }

    void setNum(int num){
        m_num=num;
    }
    int getNum(){
        return m_num;
    }
    void setRes(int res){
        m_res=res;
    }
    int getRes(){
        return m_res;
    }

private:
    int m_num,m_res;

};

class Expression{
public:
    virtual void interpreter(Context* context) =0;
};

class PlusExpression :public Expression{
public:
    virtual void interpreter(Context* context){
        int num=context->getNum();
        num++;
        context->setNum(num);
        context->setRes(num);
    }
};

class MinusExpression :public Expression{
public:
    virtual void interpreter(Context* context){
        int num =context->getNum();
        num--;
        context->setNum(num);
        context->setRes(num);
    }
};


int
main(){

    Context* pcxt =new Context(10);
    Expression* e1=new PlusExpression();

    e1->interpreter(pcxt);
    std::cout<<"PlusExpression:"<<pcxt->getRes()<<std::endl;

    Expression* e2 =new MinusExpression();
    e2->interpreter(pcxt);
    std::cout<<"MinusExpression:"<<pcxt->getRes()<<std::endl;
    delete e1,e2,pcxt;

    return 0;
}

ビルダーモード:

複雑なオブジェクトの構築プロセスをその表現から分離すると、同じ構築プロセスで異なる表現を作成できるようになります。

 ユーザーは、構築するタイプを指定するだけで、そのタイプに対応する製品インスタンスを取得でき、構築プロセスの詳細は気にする必要はありません。

つまり、複数のコンポーネントを含むオブジェクトを徐々に構築する方法であり、同じ構築プロセスで異なる製品を作成することができます。

欠点:

        1. クラス数の増加: 冗長なBuilderオブジェクトの生成

        2. 内部修正の難しさ:製品が内部的に変更された場合、それに応じてビルダーも修正する必要があります

アドバンテージ:

        1. 優れたカプセル化: 作成と使用の分離

        2. 優れたスケーラビリティ: 構築クラスは互いに独立しており、ある程度分離されています。

工場出荷時モードとの比較:

    焦点は異なります。ビルダー パターンはメソッド呼び出しプロセスに重点を置きますが、ファクトリ パターンは製品の作成に重点を置き、メソッドが使用される順序は気にしません。

    オブジェクト作成の異なる強み: オブジェクト作成の異なる強み. ビルダー パターンは、さまざまな複雑なコンポーネントで構成される複雑な製品を作成できます. ファクトリ パターンは、同じインスタンス オブジェクトを作成します。

該当するシーン:

        複雑な構造: オブジェクトは、多くの属性を持つ非常に複雑な内部構造を持っています。作成と使用を分離する: 複雑なオブジェクトの作成と使用を分離したいと考えています。

        ビルダー パターンは、オブジェクトの作成に多くの手順が必要な場合に適しています。単純な方法でオブジェクトを作成するだけの場合は、ファクトリ パターンが適しています。

実装コード:

#include<iostream>
#include<memory.h>
#include<string>

class PersonBuilder {
public:
    virtual void buildHead() {}
    virtual void buildBody() {}
    virtual void buildArm() {}
    virtual void buildLeg() {}
    PersonBuilder(){}
    PersonBuilder(std::string g, std::string p) {
        this->g = g;
        this->p = p;
    }
    virtual ~PersonBuilder() {};

    std::string g, p;
};

class PersonThinBuilder : public PersonBuilder {
public:
    PersonThinBuilder(std::string g, std::string p){
        this->g = g;
        this->p = p;
    }
    void buildHead() {
        std::cout << "瘦子 画头" << std::endl;
    }
    void buildBody() {
        std::cout << "瘦子 画身体" << std::endl;
    }
    void buildArm() {
        std::cout << "瘦子 画胳膊" << std::endl;
    }
    void buildLeg() {
        std::cout << "瘦子 画大腿" << std::endl;
    }

};

class PersonFatBuilder : public PersonBuilder {
public:
    PersonFatBuilder() {}
    PersonFatBuilder(std::string g, std::string p) {
        this->g = g;
        this->p = p;
    }
    
    void buildHead() {
        std::cout << "胖子 画头" << std::endl;
    }
    void buildBody() {
        std::cout << "胖子 画身体" << std::endl;
    }
    void buildArm() {
        std::cout << "胖子 画胳膊" << std::endl;
    }
    void buildLeg() {
        std::cout << "胖子 画大腿" << std::endl;
    }
};

class PersonDirector {
public:
    PersonDirector(PersonBuilder* pb) {
        this->pb = pb;
    }
    void createPerson() {
        pb->buildHead();
        pb->buildBody();
        pb->buildArm();
        pb->buildLeg();
    }
private:
    PersonBuilder* pb;
};


int
main() {

    PersonThinBuilder* pt = new PersonThinBuilder("画瘦子", "红笔");
    PersonDirector* pd = new PersonDirector(pt);
    pd->createPerson();

    return 0;
}

単純な工場パターン:

        パラメータ制御によりあらゆる製品を生産可能

アドバンテージ:

        シンプルで粗雑で、直感的で理解しやすい。ファクトリを使用して、同じ階層構造の下で任意の製品を生産します

欠点:

        1. すべては一緒に生産されるため、製品が多すぎると膨大な量のコードが必要になります。

        2. 開閉原理(拡張の場合は開き、変更の場合は閉じます)があまり良くないため、新しい製品を追加したい場合は、工場出荷時の方法を変更する必要があります。

工場出荷時の方法:

        オブジェクトを作成するためのインターフェイスを定義しますが、作成するオブジェクトの種類はサブクラスに決定させ、複数のファクトリを使用して指定された固定製品を生産します。

実装コード:

#include <iostream>
#include <string>
#include <memory>
class Fruit {
 public:
    Fruit(){}
    virtual void show() = 0;
};
class Apple : public Fruit {
 public:
    Apple() {}
    virtual void show() {
    std::cout << "我是⼀个苹果" << std::endl;
 }
};
class Banana : public Fruit {
 public:
    Banana() {}
    virtual void show() {
        std::cout << "我是⼀个⾹蕉" << std::endl;
    }
};
class FruitFactory {
 public:
    static std::shared_ptr<Fruit> create(const std::string &name) {
    if (name == "苹果") {
        return std::make_shared<Apple>();
    }else if(name == "⾹蕉") {
        return std::make_shared<Banana>();
    }
    return std::shared_ptr<Fruit>();
 }
};
int main()
{
    std::shared_ptr<Fruit> fruit = FruitFactory::create("苹果");
    fruit->show();
    fruit = FruitFactory::create("⾹蕉");
    fruit->show();
    return 0;
}

プロトタイプモード:

        プロトタイプ モードは自己複製の機能も提供します。これは、既存のオブジェクトを介して新しいオブジェクトを作成できることを意味します。

アドバンテージ:

    1. オブジェクトは、それが属する具体的なクラスに結合せずにクローンを作成できます。

    2. 初期化コードを繰り返し実行することを避けるために、事前に生成されたプロトタイプのクローンを作成できます。

    3. 複雑なオブジェクトの生成が容易になる

    4. 複雑なオブジェクトのさまざまな構成は、継承以外の方法で処理できます。

欠点:

    1. クラスごとにクローンメソッドを設定する必要があります

    2. clone メソッドはクラス内にあるため、既存のクラスを変更する場合はコードを変更する必要があり、開始と終了の原則に違反します。

    3. ディープ コピーを実行する場合は、より複雑なコードを記述する必要があります。また、オブジェクト間に複数のネストがある場合、ディープ クローン作成を実現するには、オブジェクトの各層に対応するクラスがディープ クローン作成をサポートする必要がありますが、これは実装が複雑です。

該当するシーン:

    1. オブジェクトが同一または類似しており、いくつかの個別の属性が異なるだけの場合

    2. 初期化時間が長い、CPU 使用率が高すぎる、ネットワーク リソースが多すぎるなど、オブジェクトの作成コストが高いため、リソースを最適化する必要があります。

    3. オブジェクトの作成には頻繁なデータの準備やアクセス許可などが必要となり、パフォーマンスの向上やセキュリティの向上が必要になります。

    4. このタイプのオブジェクトはシステムで広く使用されており、各呼び出し元はその属性を再割り当てする必要があります。

実装コード:

#include<iostream>
#include<memory.h>
#include<string>

class Prototype{
public:
    virtual ~Prototype(){}
    virtual Prototype* Clone()const=0;
protected:
    Prototype(){}
};

class ConcretePrototype:public Prototype{
public:
    ConcretePrototype(){}
    ConcretePrototype(const ConcretePrototype& cp){
        std::cout<<"ConcretePrototype copy ..."<<std::endl;
    }
    ~ConcretePrototype();
    Prototype* Clone()const{
        return new ConcretePrototype(*this);
    }
};

int 
main(){

    Prototype* p=new ConcretePrototype();
    Prototype* p1=p->Clone();

    return 0;
}

シングルトンモード:

        クラスは一意のオブジェクトのみを作成します。つまり、オブジェクトは一度作成すれば複数回使用できます。

アドバンテージ:

    1. 固有のインスタンスへの制御されたアクセスを提供します

    2. システム内にオブジェクトが 1 つだけあるため、システム リソースが節約でき、頻繁に作成および破棄する必要がある一部のオブジェクトについては、シングルトン モードによりシステムのパフォーマンスが確実に向上します。

    3.インスタンスの可変数を許可する

欠点:

    1. シングルトン パターンには抽象化層がないため、シングルトン クラスを拡張するのは非常に困難です。

    2. シングルトンクラスの告発は重すぎ、「単一責任原則」にある程度違反します。

    3. シングルトンを悪用すると、リソースを節約するためにデータベース接続プール オブジェクトがシングルトン クラスとして設計され、接続プール オブジェクトを共有するプログラムが多すぎる可能性があるなど、いくつかのマイナスの問題が発生します。

    また、接続プールのオーバーフローが発生し、インスタンス化されたオブジェクトが長期間使用されないと、システムはそれをゴミとみなし、リサイクルされ、オブジェクトの状態が失われます。

ステップ:

    1. 建設業者の民営化

    2. 現在のクラスの静的プライベート ポインター変数を追加します。

    3. 静的外部インターフェイスを提供し、ユーザーがシングルトン モードを取得できるようにします。

詳細:

    1. ハングリー モード: クラスのロード時にインスタンスを作成するこのモードの利点は、マルチスレッド アクセスの問題を考慮する必要がなく、インスタンスの一意性を確保できることです。

        最初から作成するため、通話速度や応答時間の点で怠惰なスタイルよりも優れています。ただし、システムが実行時にインスタンス オブジェクトを使用する必要があるかどうか

        クラスのロード時にオブジェクトが作成されますが、リソースの利用効率という点では、Hungry スタイルは Lazy スタイルほど良くありません。また、システムのロード時にシングルトン オブジェクトを作成する必要があるため、ロード時間が長くなります。比較的長い。

    2. 遅延モード: 初めて使用するときに作成され、常にシステム リソースを占有する必要がなく、遅延読み込みが実現されますが、特に次の場合、複数のスレッドによる同時アクセスの問題に対処する必要があります。

        リソース コントローラーとして、シングルトン クラスはインスタンス化中にリソースの初期化を行う必要があり、リソースの初期化には時間がかかる可能性が高く、複数のスレッドが初めて同時に発生する可能性があります。

        このようなクラスを参照する可能性が高くなるため、二重チェック メカニズムを通じて制御する必要があり、システムのパフォーマンスに影響を与える可能性があります。

実装コード:

#include<iostream>
#include<memory.h>
#include<string>
//饿汉式
class SingletonHungry{
public:
    static SingletonHungry* getInstance(){
        return pSingleton;
    }
private:
    SingletonHungry(){}
    static SingletonHungry* pSingleton;
};
SingletonHungry* SingletonHungry::pSingleton=new SingletonHungry;//在加载的时候就创建

class SingletonLazy{
public:
    static SingletonLazy* getInstance(){
        if(pSingleton==nullptr){
            pSingleton=new SingletonLazy;
        }
        return pSingleton;
    }
private:
    SingletonLazy(){}
    static SingletonLazy* pSingleton;
};
SingletonLazy* SingletonLazy::pSingleton=nullptr;



int 
main(){

    SingletonHungry* test1=SingletonHungry::getInstance();
    SingletonHungry* test2=SingletonHungry::getInstance();
    if(test1==test2) std::cout<<"饿汉单例模式"<<std::endl;
    else std::cout<<"Not 饿汉单例模式"<<std::endl;

    SingletonLazy* test3=SingletonLazy::getInstance();
    SingletonLazy* test4=SingletonLazy::getInstance();
    if(test3==test4) std::cout<<"懒汉单例模式"<<std::endl;
    else std::cout<<"Not 懒汉单例模式"<<std::endl;



    return 0;
}

-------------------私が境界線、次は構造パターン ------------------ - ----

アダプターモード:

        2 つの独立したインターフェイスの機能を結合し、あるクラスのインターフェイスを顧客が期待する別のインターフェイスに変換して、本来互換性のないクラスや機能が一緒に動作できるようにし、互換性のない 2 つのインターフェイス間のブリッジとなります。

アドバンテージ:

    1. インターフェースの互換性の問題を効果的に解決できる

    2. 強い柔軟性

欠点:

    1. アダプターを過度に使用すると、システムが非常に乱雑になり、全体を把握することが困難になります。

        ​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​ ​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

        したがって、必要がなければアダプタを使用せずに直接システムを再構築することも可能です。

該当するシーン:

    1. ソフトウェアの反復プロセス中に、インターフェイスの非互換性が発生します。

    2. 将来導入される可能性のあるクラスなど、相互にあまり関係のないクラスを操作するために再利用できるクラスを作成したい これらのソース クラスは、必ずしも一貫したインターフェイスを持っているとは限りません。

    3. インターフェイス変換を通じてクラスを別のクラスに挿入します。

    (たとえば、トラと鳥の間には、フライング タイガーが存在します。エンティティを追加する必要はなく、アダプターを追加してタイガー オブジェクトを含め、フライング インターフェイスを実装します。)

予防:

        アダプターはプログラム設計中に追加されるのではなく、現在サービス中のプロジェクトの問題を解決するために追加されます。

実装コード:

#include<iostream>
#include<memory.h>
#include<string>

class Target{
public:
    Target(){}
    virtual ~Target(){}
    virtual void Request(){
        std::cout<<"Target::Request"<<std::endl;
    }
};

class Adaptee{
public:
    Adaptee(){}
    ~Adaptee(){}
    void SpecificRequest(){
        std::cout<<"Adaptee::SpecificRequest"<<std::endl;
    }
};

class Adapter:public Target{
public:
    Adapter(Adaptee* ade){
        _ade=ade;
    }
    ~Adapter(){}
    void Request(){
        _ade->SpecificRequest();
    }
private:
    Adaptee* _ade;
};



int
main(){

    Adaptee* ade=new Adaptee;
    Target* adt =new Adapter(ade);
    adt->Request();
    return 0;
}

ブリッジモード:

        抽象化と実装を分離し、両者を独立して変更できるようにします。ブリッジ モードは、継承関係を結合関係に変換します。抽象化と実装の分離を達成

アドバンテージ:

    1. オブジェクトの特定の実装が抽象化に依存するように現実を抽出し、抽象化を実装します。これにより、依存性逆転の原則が満たされます。

    2. コード内の重複情報を減らすために共有できる変更を抽出します。

    3. オブジェクトの特定の実装はより柔軟になり、複数の要素の要件を満たすことができます。

    4. システムのスケーラビリティの向上: 特定のディメンションを拡張する必要がある場合、実装クラス インターフェイスまたは特定の実装クラスを追加するだけで済み、他のディメンションには影響を与えず、開閉の原則に従います。

欠点:

    1. 抽象層で関係性が確立され、最初から抽象層で設計・プログラミングする必要があるため、システムの理解・設計の難易度が高くなります。

    2. システム内の独立して変化する 2 つ以上の次元を正確に識別する必要があるが、システム内の 2 つの次元を正確に識別する方法はブリッジ モードの適用の難しさです。

該当するシーン:

    1. システムは、2 つのレベル間に静的な継承関係が確立されるのを避けるために、抽象化と具体化の間に柔軟性を追加する必要があります。ブリッジ モードを通じて、抽象化層で関連付け関係を確立できます。

    2. クラスには独立して変化する 2 つ以上の次元があり、これら 2 つ以上の次元を独立して拡張する必要がある

    3. 継承を使用したくないシステム、またはマルチレベルの継承によりシステム クラスの数が急激に増加するシステムの場合。

//Windows の描画ソフトウェアを例に挙げます。 Pen は drow インターフェイスを外部に公開する基本クラスで、描画される線は太、中、細です。色は赤、黄、青に分けられます。継承を通じて実装された場合。この場合、順列と組み合わせには 3*3 = 9 個のサブクラスが必要になります。代わりにブリッジ モードを使用してください。必要なのは6つだけです。明らかに、厚さと色が多ければ多いほど、利点は大きくなります。

実装コード:

#include<iostream>
#include<string>
class AbstractionImp {
public:
    virtual ~AbstractionImp() {}
    virtual void Operation() = 0;
protected:
    AbstractionImp() {}
};

class Abstraction {
public:
    virtual ~Abstraction() {}
    virtual void Operation() = 0;
protected:
    Abstraction() {}
};

class RefinedAbstaction :public Abstraction {
public:
    RefinedAbstaction(AbstractionImp* imp) {
        _imp = imp;
    }
    ~RefinedAbstaction() {}
    void Operation() {
        _imp->Operation();
    }
private:
    AbstractionImp* _imp;
};

class ConcreteAbstractionImpA : public AbstractionImp {
public:
    ConcreteAbstractionImpA() {}
    ~ConcreteAbstractionImpA() {}
    virtual void Operation() {
        std::cout << "ConcreteAbstractionImpA...." << std::endl;
    }

};

class ConcreteAbstractionImpB : public AbstractionImp {
public:
    ConcreteAbstractionImpB() {}
    ~ConcreteAbstractionImpB() {}
    virtual void Operation() {
        std::cout << "ConcreteAbstractionImpB...." << std::endl;
    }
};

int
main() {

    AbstractionImp* imp = new ConcreteAbstractionImpA();
    Abstraction* abs = new RefinedAbstaction(imp);
    abs->Operation();

    return 0;
}

結合エンティティ モード:

        これは、オブジェクトをツリー構造に結合し、独立したオブジェクトのように使用するために使用できる構造設計パターンです。

アドバンテージ:

    1. クライアントが単一のオブジェクトと結合されたオブジェクトを統一的な方法で処理できるようにします。

    2. 新しいタイプのコンポーネントをより簡単に追加できるようになります

欠点:

    1. デザインが一般的すぎる可能性があります。場合によっては、リーフ コンポーネントのみで特定の操作を定義する必要がありますが、共通性があるため、これらの操作をすべてのコンポーネントで定義する必要があります。

該当するシーン:

    例: グラフィカル ユーザー インターフェイスでは、多くのグラフィカル ユーザー インターフェイスは組み合わせモードを使用して、ウィンドウやパネルなどのコンポーネントを編成および管理します。ファイル システムは通常、構成パターンを使用してファイルとフォルダーの階層を表現します。

        XML/HTML ドキュメントは、組み合わせモードを使用してツリー構造を表現することもできます。

実装コード:

#include<iostream>
#include<algorithm>
#include<memory.h>
#include<string>
#include<list>

class Component{
public:
    Component(){}
    virtual ~Component(){}
    virtual void Operation()=0;
    virtual void Add(Component* pChild){};
    virtual void Remove(Component* pChild){};
    virtual Component* GetChild(int nIndex){ 
        return nullptr;
    };

};

class Leaf: public Component{
public:
    Leaf(){}
    virtual ~Leaf(){}
    virtual void Operation(){
        std::cout<<"Operation by leaf\n";
    }
};

class Composite:public Component{
public:
    Composite(){}
    virtual ~Composite(){
        std::list<Component*>::iterator iter1,iter2,temp;
        for(iter1 =m_ListOfComponent.begin(),iter2=m_ListOfComponent.end();iter1!=iter2;){
            temp=iter1;
            ++iter1;
            delete *temp;
        }
    }
    virtual void Operation(){
        std::cout<<"Operation by Composite\n";
        std::list<Component*>::iterator iter1,iter2;
        for( iter1=m_ListOfComponent.begin(), iter2=m_ListOfComponent.end(); iter1!=iter2; ++iter1){
            (*iter1)->Operation();
        }
    }
    virtual void Add(Component* pChild){
        m_ListOfComponent.push_back(pChild);
    }
    virtual void Remove(Component* pChild){
        std::list<Component*>::iterator iter;
        iter=find(m_ListOfComponent.begin(),m_ListOfComponent.end(),pChild);
        if(m_ListOfComponent.end()!=iter){
            m_ListOfComponent.erase(iter);
        }
    }
    virtual Component* GetChild(int nIndex){
        if(nIndex <=0 ||nIndex > m_ListOfComponent.size()) return nullptr;
        std::list<Component*>::iterator iter1,iter2;
        int i;
        for(i=1,iter1=m_ListOfComponent.begin(),iter2=m_ListOfComponent.end() ; iter1!=iter2 ; ++iter1, ++i){
            if(i == nIndex) break;
        }
        return *iter1;
    }
private:
    std::list<Component*> m_ListOfComponent;
};


int 
main(){

    Leaf* pLeaf1=new Leaf();
    Leaf* pLeaf2=new Leaf();
    Composite* pComposite=new Composite;
    pComposite->Add(pLeaf1);
    pComposite->Add(pLeaf2);
    pComposite->Operation();
    pComposite->GetChild(2)->Operation();
    
    return 0;
}

デコレータパターン:

        元のクラス ファイルを変更したり、継承を使用したりせずに、オブジェクトを動的に拡張してオブジェクトを拡張または追加します。

アドバンテージ:

    1. 優れた柔軟性: デコレータ モードは継承に比べて、オブジェクトの機能をより柔軟に拡張できます。

    2. 優れたスケーラビリティ、さまざまな装飾の組み合わせによりさまざまなオブジェクトを作成でき、クラスの爆発を回避できます。

    3. 開閉の原則を満たし、デザインパターンの要件を満たす再利用の原則を満たします。

    4. 優れた透明性: クライアントは抽象的な操作をターゲットにしており、特定の実装コンテンツには見えません。

欠点:

    1. 高い複雑さ。デコレータの設計は非常に複雑であることが多く、高度な開発スキルが必要です。

該当するシーン:

    たとえば、グラフィカル ユーザー インターフェイス: 多くのグラフィカル ユーザー インターフェイスは、デコレーター パターンを使用して、新しい動作やスタイルを動的に追加します。データ圧縮、ロギングなど。

実装コード:

#include<iostream>
#include<memory>
#include<string>

class AbstractHero{
public:
    virtual void showStatus()=0;
    int mHp,mMp,mAt,mDf;
};

class HeroA:public AbstractHero{
public:
    HeroA(){
        mHp=0,mMp=0,mAt=0,mDf=0;
    }
    virtual void showStatus(){
        std::cout<<"血量:"<<mHp<<std::endl;
        std::cout<<"魔法:"<<mMp<<std::endl;
        std::cout<<"攻击:"<<mAt<<std::endl;
        std::cout<<"防御:"<<mDf<<std::endl;
    }
};

//英雄穿上某个装饰物,那么也还是一个英雄
class AbstractEquipment: public AbstractHero{
public:
    AbstractEquipment(AbstractHero* hero){
        pHero=hero;
    }
    virtual void showStatus(){}
    AbstractHero* pHero;
};

//给英雄穿上狂徒铠甲
class KuangtuEquipment:public AbstractEquipment{
public:
    KuangtuEquipment(AbstractHero* hero):AbstractEquipment(hero){}
    //增加新的功能
    void AddKuangtu(){
        std::cout<<"新英雄穿上狂徒之后"<<std::endl;
        this->mHp=this->pHero->mHp;
        this->mMp=this->pHero->mMp;
        this->mAt=this->pHero->mAt;
        this->mDf=this->pHero->mDf+30;
    }
    virtual void showStatus(){
        AddKuangtu();
        std::cout<<"血量:"<<mHp<<std::endl;
        std::cout<<"魔法:"<<mMp<<std::endl;
        std::cout<<"攻击:"<<mAt<<std::endl;
        std::cout<<"防御:"<<mDf<<std::endl;
    }
};


int main(){

    AbstractHero* hero=new HeroA;
    hero->showStatus();
    std::cout<<"------------------------"<<std::endl;
    //给英雄穿上狂徒
    hero=new KuangtuEquipment(hero);
    hero->showStatus();

    return 0;
}

外観モード(ファサードモード):

        外部とサブシステム間の通信は、統一された外観のオブジェクトを通じて実行する必要があることを指します。

        サブシステム内の一連のインターフェイスに一貫したインターフェイスを提供し、サブシステムを使いやすくする高レベルのインターフェイスを定義します。

アドバンテージ:

    1. クライアントとサブシステム間の対話を簡素化し、クライアントがサブシステムを使いやすくします。

    2. クライアントとサブシステム間の結合を軽減できるため、クライアントはサブシステムと直接対話する必要がなくなります。

    3. クライアントに影響を与えることなく、サブシステムの実装を変更できる

欠点:

    1. クライアントは外観クラスを通じてのみサブシステムにアクセスできるため、サブシステムへのクライアントのアクセスが制限される場合があります。

    2. 設計に注意しないと、保守が困難な巨大で複雑なクラスになる可能性があります。

該当するシーン:

    1. ソフトウェア開発では、外観パターンは、基礎となるライブラリまたはフレームワークをカプセル化し、上位層に単純なインターフェイスを提供するためによく使用されます。これにより、上位層アプリケーションと基礎となるライブラリ間の結合が軽減され、上位層アプリケーションの開発と保守が容易になります。

    2. オペレーティング システムでは、アピアランス モードも広く使用されることがよくあります。たとえば、オペレーティング システムは、アプリケーションがハードウェア ドライバーと直接対話せずにハードウェア リソースに簡単にアクセスできるように、アプリケーションにシンプルなインターフェイスを提供します。

    3. Web 開発では、通常、Web フレームワークは開発者にシンプルなインターフェイスを提供するため、開発者は基礎となる http プロトコルやサーバー構成を気にせずに Web アプリケーションを簡単に開発できます。

実装コード:

#include<iostream>
#include<string>
#include<memory>

class Television{
public:
    void on(){
        std::cout<<"电视机打开"<<std::endl;
    }
    void off(){
        std::cout<<"电视机关闭"<<std::endl;
    }
};

class Light{
public:
    void on(){
        std::cout<<"灯打开"<<std::endl;
    }
    void off(){
        std::cout<<"灯关闭"<<std::endl;
    }
};

class DVDplayer{
public:
    void on(){
        std::cout<<"DVD打开"<<std::endl;
    }
    void off(){
        std::cout<<"DVD关闭"<<std::endl;
    }
};

//外观模式
class KTVMode{
public:
    KTVMode(){
        pTV=new Television;
        pLight=new Light;
        pDvD=new DVDplayer;
    }
    void onKTV(){
        pTV->on();
        pLight->on();
        pDvD->on();
    }
    ~KTVMode(){
        pTV->off();
        pLight->off();
        pDvD->off();
        delete pTV,pLight,pDvD;
    }
private:
    Television* pTV;
    Light* pLight;
    DVDplayer* pDvD;
};


int
main(){

    KTVMode* ktv1=new KTVMode;
    ktv1->onKTV();
    delete ktv1;

    return 0;
}

フライウェイトモード:

        軽量という意味で、中国語では香遠と訳されます。共有テクノロジーを通じて同一または類似の再利用を可能にします。共有テクノロジーを使用して、多数のきめ細かいオブジェクトの再利用を効果的にサポートします。

アドバンテージ:

    1. メモリ使用量を削減し、プログラムのパフォーマンスを向上させることができます。オブジェクトを共有することでオブジェクトの数が減り、メモリ使用量が削減されます。

    2. プログラムの実行速度を向上させることができます。オブジェクトが少ないため、プログラムが実行時に処理する必要があるオブジェクトも少なくなり、プログラムの実行速度が速くなります。

欠点:

    1. フライウェイト モードではプログラムが複雑になります。フライウェイト オブジェクトを実装する場合は、オブジェクトの状態を内部状態と外部状態に分離し、内部状態を共有する方法を検討する必要があります。

    2. プログラムのメンテナンスコストが増加する可能性がある フライウェイト モードではオブジェクトの作成と使用が分離されるため、フライウェイト モード オブジェクトを使用する場合は、クライアント コードで外部状態を管理し、クライアント コードに外部状態を渡す必要があります。これにより、プログラムのメンテナンスコストが増加する可能性があります

該当するシーン:

    1. アプリケーションは多数の同一または類似のオブジェクトを使用するため、ストレージのオーバーヘッドが大きくなります。

    2. オブジェクトの状態のほとんどは外部化でき、これらの外部状態をオブジェクトに渡すことができます。

    3. オブジェクトの外部状態を削除すると、多くのオブジェクト グループを比較的少数の共有オブジェクトに置き換えることができます。

    4. アプリケーションはオブジェクトの識別に依存しません。フライウェイト オブジェクトは共有できるため、概念的に明らかな他のオブジェクトに対してアイデンティティ テストは true を返します。

    5. フライウェイト モードを使用するには、フライウェイト オブジェクトを保存するためのフライウェイト プールを維持する必要があり、リソースが消費されるため、フライウェイト オブジェクトが複数回再利用される場合にのみフライウェイト モードを使用する価値があります。

実装コード:

#include<iostream>
#include<list>
#include<algorithm>
class ConcreateFlyweight;
class Flyweight{
public:
    virtual ~Flyweight(){}
    std::string GetIntrinsicState(){
        return m_State;
    }
    virtual void Operation(std::string& ExtrinsicState)=0;
protected:
    Flyweight(const std::string& state):m_State(state){}
private:
    std::string m_State;
};

class ConcreateFlyweight:public Flyweight{
public:
    ConcreateFlyweight(const std::string& state):Flyweight(state){}
    virtual ~ConcreateFlyweight(){}
    virtual void Operation(std::string& ExtrinsicState){}
};

class FlyweightFactory{
public:
    FlyweightFactory(){}
    ~FlyweightFactory(){
        for(std::list<Flyweight*>::iterator iter1=m_ListFlyweight.begin(), temp; iter1!=m_ListFlyweight.end(); ){
            temp=iter1;
            ++iter1;
            delete *temp;
        }
        m_ListFlyweight.clear();
    }
    Flyweight* GetFlyweight(const std::string& key){
        for(std::list<Flyweight*>::iterator iter1=m_ListFlyweight.begin(); iter1!=m_ListFlyweight.end(); ++iter1){
            if((*iter1)->GetIntrinsicState() == key){
                std::cout<<"The Flyweight:"<< key<<" already exits " <<std::endl;
                return (*iter1);
            }
        }
        std::cout<<"Creating a new Flyweight:"<<key <<std::endl;
        Flyweight* flyweight =new ConcreateFlyweight(key);
        m_ListFlyweight.push_back(flyweight);
    }
private:
    std::list<Flyweight*> m_ListFlyweight;
};


int
main(){

    FlyweightFactory flyweightfactory;
    flyweightfactory.GetFlyweight("hello");
    flyweightfactory.GetFlyweight("world");
    flyweightfactory.GetFlyweight("hello");

    return 0;
}

エージェントモード:

        ソフトウェアを変更せずに元のオブジェクトへのアクセスを制御するプロキシを提供します

アドバンテージ:

    1. 告発内容は明確であり、本当のターゲットは自身のビジネス ロジックに焦点を当てており、責任を持たない他のコンテンツを考慮する必要はなく、エージェントに任せて完了させます。

    2. 高い拡張性、実際のオブジェクトへの変更はエージェントに影響を与えません

    3. デカップリング、クライアントを実際のオブジェクトから分離し、システムの結合を軽減します。

    4. パフォーマンスの向上、仮想エージェントによるシステム リソースの消費量の削減

    5. 高いセキュリティと安定性。エージェントはアクセスをより適切に制御し、システムのセキュリティを向上させることができます。

欠点:

    1. システムが複雑になります。エージェントには多くの責任があります

    2. リクエスト速度が遅い。クライアントや実オブジェクトにプロキシを追加すると、システムプロセス全体の動作効率がある程度低下します。

該当するシーン:

    元のオブジェクトへのアクセスを制御する必要がある場合、追加機能を提供する場合、クライアント コードの複雑さを軽減する場合など、プロキシ モードを使用できます。

実装コード:

#include<iostream>
#include<string>
#include<memory>

class AbstractCommonInterface{
public:
    virtual void run() =0;
};

class MySystem:public AbstractCommonInterface{
public:
    virtual void run(){
        std::cout<<"系统启动"<<std::endl;
    }
};

class MySystemProxy:public AbstractCommonInterface{
public:
    MySystemProxy(std::string username,std::string password){
        mUserName=username;
        mPassWord=password;
        pMySystem=new MySystem;
    }

    bool Check(){
        if(mUserName=="admin" && mPassWord=="admin"){
            return true;
        }
        return false;
    }

    virtual void run(){
        if(Check()==true){
            std::cout<<"启动成功"<<std::endl;
            pMySystem->run();
        }else{
            std::cout<<"用户名或密码错误\n";
        }
    }
    ~MySystemProxy(){
        if(pMySystem !=nullptr){
            delete pMySystem;
        }
    }

private:
    std::string mUserName,mPassWord;
    MySystem* pMySystem;
};


int
main(){

    MySystemProxy* proxy =new MySystemProxy("admin","admin");
    proxy->run();
    return 0;
}

------------------------私は境界線です、以下は行動パターンです----------- -- ------

責任連鎖モデル:

        リクエストの受信者オブジェクトのチェーンを作成します。このパターンはリクエストのタイプを示し、リクエストの送信者と受信者を分離します。

アドバンテージ:

    1. 結合度を下げて、リクエストの送信者と受信者を分離します。

    2. オブジェクトへの責任の割り当ての柔軟性が強化されました。チェーン内のメンバーを変更したり、その順序を移動したりすることで、責任を動的に追加または削除できます。

    3. 新しいリクエスト処理クラスを追加すると非常に便利です

欠点:

    1. リクエストには明確な受信者が存在しないため、リクエストが処理されるという保証はありません。

    2. システムのパフォーマンスにある程度の影響があり、コードのデバッグ時に不便になり、ループ呼び出しが発生する可能性があります。

    3. 実行時の特性を観察することが難しく、エラーが発生する可能性があります。

該当するシーン:

    たとえば、Java の例外処理メカニズムでは、メソッドが例外をスローしたときに、そのメソッドが内部で例外を処理しない場合、その例外は、処理されるかスローされるまで、そのメソッドを呼び出す上位のメソッドに渡されて処理されます。プログラムの最外層

    JavaScriptのイベントバブリングメカニズム

    サーブレット開発におけるフィルター チェーン

実装コード:

#include<iostream>
#include<algorithm>
#include<vector>
#include<string>

class PurchaseRequest{
public:
    int getType()const{
        return type;
    }
    float getPrice()const{
        return price;
    }
    int getId()const{
        return id;
    }
    PurchaseRequest(const int type,const float price,const int id):type(type),price(price),id(id){

    }
private:
    int type,id;
    float price;
};

class Approver{
public:
    void setApprover(Approver* const approver){
        this->approver=approver;
    }
    explicit Approver(const std::string& name):name(name){}
    virtual void processRequest(PurchaseRequest* purchaseRequest)=0;
protected:
    Approver* approver;
    std::string name;
};

class DepartmentApprover:public Approver{
public:
    explicit DepartmentApprover(const std::string& name):Approver(name){}
    void processRequest(PurchaseRequest* purchaseRequest)override{
        if(purchaseRequest->getPrice()<=5000){
            std::cout<<"请求编号id= "<<purchaseRequest->getId()<<"被"<<this->name<<"处理"<<std::endl;
        }else{
            approver->processRequest(purchaseRequest);
        }
    }
};

class CollegeApprover:public Approver{
public:
    explicit CollegeApprover(const std::string& name):Approver(name){}
    void processRequest(PurchaseRequest* purchaseRequest)override{
        if(purchaseRequest->getPrice()>5000 && purchaseRequest->getPrice()<=10000){
            std::cout<<"请求编号id= "<<purchaseRequest->getId()<<"被"<<this->name<<"处理"<<std::endl;
        }else{
            approver->processRequest(purchaseRequest);
        }
    }
};

class ViceSchoolMasterApprover:public Approver{
public:
    explicit ViceSchoolMasterApprover(const std::string& name):Approver(name){}
    void processRequest(PurchaseRequest* purchaseRequest)override{
        if(purchaseRequest->getPrice()>10000 && purchaseRequest->getPrice()<=30000){
            std::cout<<"请求编号id= "<<purchaseRequest->getId()<<"被"<<this->name<<"处理"<<std::endl;
        }else{
            approver->processRequest(purchaseRequest);
        }
    }
};


int
main(){

    PurchaseRequest* purchaseRequest =new PurchaseRequest(1,10005,1);
    DepartmentApprover* department =new DepartmentApprover("庄主任");
    CollegeApprover* college=new CollegeApprover("李院长");
    ViceSchoolMasterApprover* viceSchoolMaster =new ViceSchoolMasterApprover("王副校长");

    department->setApprover(college);
    college->setApprover(viceSchoolMaster);

    department->processRequest(purchaseRequest);//部长审批不了转交给学院,学院审批不来转交给副校长,副校长能审批批下来了

    return 0;
}

コマンドモード:

        リクエストをオブジェクトとしてカプセル化し、リクエストを作成する責任とリクエストを実行する責任を分離します。

アドバンテージ:

    1. 優れたカプセル化、各コマンドはカプセル化されているため、クライアントは、何らかの機能が必要な場合、コマンドがどのように実行されるかを知らなくても、対応するコマンドを呼び出すことができます。

欠点:

    1. 一部のシステムでは特定のコマンド クラスが多すぎる可能性があります。コマンドごとに特定のコマンド クラスを設計する必要があるため、一部のシステムでは多数の特定のコマンド クラスが必要になる場合があり、これはコマンド モードの使用に影響します。

該当するシーン:

    システムは、発信者と受信者が直接対話しないように、要求者と要求受信者を切り離す必要があります。

    システムは、さまざまな時点でリクエストを指定し、リクエストをキューに入れてリクエストを実行する必要があります。

    システムはコマンドの元に戻す操作と復元操作をサポートする必要があります。

    (コマンドを考慮する場合はどこでもコマンド モードを使用できます)

実装コード:

#include<iostream>
#include<vector>
#include<List>
#include<memory>
#include<queue>
#include<windows.h>

class HandleClinetProtocal{
public:
    void AddMoney(){
        std::cout<<"给玩家增加金币"<<std::endl;
    }
    void AddDiamond(){
        std::cout<<"给玩家增加钻石"<<std::endl;
    }
};

class AbstractCommand{
public:
    virtual void handle()=0;
};

class AddMoneyCommand:public AbstractCommand{
public:
    AddMoneyCommand(HandleClinetProtocal* protocal){
        this->pProtocol=protocal;
    }
    virtual void handle(){
        this->pProtocol->AddMoney();
    }
    HandleClinetProtocal* pProtocol;
};

class AddDiamondCommand:public AbstractCommand{
public:
    AddDiamondCommand(HandleClinetProtocal* protocal){
        this->pProtocol=protocal;
    }
    virtual void handle(){
        this->pProtocol->AddMoney();
    }
    HandleClinetProtocal* pProtocol;
};

//服务器程序(命令调用类)
class Server{
public:
    void addRequest(AbstractCommand* command){
        mCommands.push(command);
    }

    //启动处理程序
    void startHandle(){
        while(!mCommands.empty()){
            Sleep(1000);
            AbstractCommand* command =mCommands.front();
            command->handle();
            mCommands.pop();
        }
    }
    std::queue<AbstractCommand*> mCommands;
};


int
main(){

    HandleClinetProtocal* protocal=new HandleClinetProtocal;

    //客户端增加金币的请求
    AbstractCommand* addmoney =new AddMoneyCommand(protocal);

    //客户端增加钻石的请求
    AbstractCommand* adddiamond =new AddDiamondCommand(protocal);

    //将客户端的请求加入到请求队列中
    Server* server =new Server;
    server->addRequest(addmoney);
    server->addRequest(adddiamond);

    //服务器开始处理请求
    server->startHandle();

    return 0;
}

通訳モード:

        与えられた言語 (式) を指し、その文法の表現を定義し、インタプリタを使用してその言語の文 (式) を解釈することを指します。

アドバンテージ:

    1.拡張性に優れています。文法変換はクラスを通じて実装され、クラスを拡張すると解釈機能を拡張できます。

    2. 実装難易度が低い 構文ツリー内の各式ノードクラスは一定の類似性を持っているため、比較的実装が容易です。

欠点:

    1. 実行効率が低い。通常、インタプリタ内にはループや再帰文が多数存在し、解釈される文が複雑な場合、プログラムのパフォーマンスに大きな影響を与えます。

    2. クラス拡張の問題。ルールが増えるとクラスの数も増えます。

該当するシーン:

    特定の種類の問題が頻繁に発生する場合、たとえば、簡単な構文の説明、コンパイラ、演算式の計算、正規表現、ログ処理: スクリプト言語やプログラミング言語を使用してログを処理する場合、

    大量のレポートが生成されるため、ログを解析してレポートを生成する必要があります。

    各サービスのログ形式は異なりますが、データの要素は同じです。この場合、プログラムを通じて上記の問題を解決する主な解決策は、インタープリター モードを使用することです。インタープリター モードは、日常のプロジェクトではほとんど使用されません。

アダプター パターンと比較します。 2 つのパターンは似ていますが、アダプター パターンではアダプターのルールについての事前の知識は必要ありません。インタプリタモードでは、事前にルールを記述し、そのルールに従って解釈を実行する必要があります。

実装コード:

#include<iostream>
#include<list>
#include<vector>
#include<algorithm>

class Context{
public:
    Context(int num){
        m_num=num;
    }

    void setNum(int num){
        m_num=num;
    }
    int getNum(){
        return m_num;
    }
    void setRes(int res){
        m_res=res;
    }
    int getRes(){
        return m_res;
    }

private:
    int m_num,m_res;

};

class Expression{
public:
    virtual void interpreter(Context* context) =0;
};

class PlusExpression :public Expression{
public:
    virtual void interpreter(Context* context){
        int num=context->getNum();
        num++;
        context->setNum(num);
        context->setRes(num);
    }
};

class MinusExpression :public Expression{
public:
    virtual void interpreter(Context* context){
        int num =context->getNum();
        num--;
        context->setNum(num);
        context->setRes(num);
    }
};


int
main(){

    Context* pcxt =new Context(10);
    Expression* e1=new PlusExpression();

    e1->interpreter(pcxt);
    std::cout<<"PlusExpression:"<<pcxt->getRes()<<std::endl;

    Expression* e2 =new MinusExpression();
    e2->interpreter(pcxt);
    std::cout<<"MinusExpression:"<<pcxt->getRes()<<std::endl;
    delete e1,e2,pcxt;

    return 0;
}

イテレータパターン:

        コレクション オブジェクトの基になる表現を知らなくても、一貫したメソッドを使用してコレクション要素を走査するための統合インターフェイスを提供します。これらのオブジェクトが何であってもトラバースする必要がある場合は、反復子パターンの使用を選択する必要があります。

アドバンテージ:

    1. 内部表現を公開せずに、さまざまな方法で集合オブジェクトを走査することをサポートします。

    2. イテレータは集約クラスを簡素化します

    3. イテレータ パターンでは、抽象クラスの導入により、元のコードを変更せずに新しい集約クラスとイテレータを追加することが非常に便利です。

欠点:

    1. 抽象化レイヤーの追加により、システムの複雑さが増加します。

    2. 単純な走査 (配列など) の場合、反復子を使用して走査するのはより面倒です。

該当するシーン:

    リンクされたリスト、配列、ツリーなどのさまざまなコンテナを走査するためによく使用されます。 STL コンテナでは、反復子パターンが広く使用されています。

    STL のコンテナ (ベクター、リスト、セット) はすべて、コンテナ内のすべての要素を走査してアクセスするために使用できるイテレータを提供します。

実装コード:

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<list>

class Iterator{//迭代抽象类,用于定义得到开始对象、得到下一个对象、判断是否到结尾、当前对象等抽象方法,统一接口
public:
    Iterator(){}
    virtual ~Iterator(){}
    virtual std::string First() =0;
    virtual std::string Next() =0;
    virtual std::string CurrentItem() =0;
    virtual bool IsDone() =0;
};

class Aggregate{//聚集抽象类
public:
    virtual int Count() =0;
    virtual void Push(const std::string& strValue) =0;
    virtual std::string Pop(const int nIndex) =0;
    virtual Iterator* CreateIterator() =0;
};

class ConcreteIterator:public Iterator{
public:
    ConcreteIterator(Aggregate* pAggregate):m_nCurrent(0),Iterator(){
        m_Aggregate =pAggregate;
    }
    std::string First(){
        return m_Aggregate->Pop(0);
    }
    std::string Next(){
        std::string strRet;
        m_nCurrent++;
        if(m_nCurrent < m_Aggregate->Count()){
            strRet =m_Aggregate->Pop(m_nCurrent);
        }
        return strRet;
    }
    std::string CurrentItem(){
        return m_Aggregate->Pop(m_nCurrent);
    }
    bool IsDone(){
        return ((m_nCurrent >= m_Aggregate->Count()) ? true :false);
    }
private:
    Aggregate* m_Aggregate;
    int m_nCurrent;
};

class ConcreteAggregate:public Aggregate{
public:
    ConcreteAggregate():m_pIterator(nullptr){
        m_vecItems.clear();
    }
    ~ConcreteAggregate(){
        if(m_pIterator){
            delete m_pIterator;
            m_pIterator=nullptr;
        }
    }
    Iterator* CreateIterator(){
        if(!m_pIterator){
            m_pIterator=new ConcreteIterator(this);
        }
        return m_pIterator;
    }
    int Count(){
        return m_vecItems.size();
    }
    void Push(const std::string& strValue){
        m_vecItems.push_back(strValue);
    }
    std::string Pop(const int nIndex){
        std::string strRet;
        if(nIndex < Count()){
            strRet =m_vecItems[nIndex];
        }
        return strRet;
    }

private:
    std::vector<std::string> m_vecItems;
    Iterator* m_pIterator;
};


int 
main(){

    ConcreteAggregate* pName=new ConcreteAggregate();
    if(pName){
        pName->Push("hello");
        pName->Push("word");
        pName->Push("cxue");
    }
    Iterator* iter =nullptr;
    iter= pName->CreateIterator();
    if(iter){
        std::string strItem =iter->First();
        while(!iter->IsDone()){
            std::cout<< iter->CurrentItem()<<" is ok "<<std::endl;
            iter->Next();
        }
    }
    return 0;
}

メディエーターモデル:

        これは、アービター パターンとしても知られる動作的なソフトウェア デザイン パターンであり、その名前が示すように、このパターンの役割は、他のクラスが適切に通信できるよう仲介者として機能することです。

アドバンテージ:

    1. 分離: 仲介者の存在により、同僚オブジェクト間の強い結合関係が緩和され、全体に影響を与えることなく独立して変更できるため、再利用が容易になります。

    2. 優れた拡張性。インタラクションの動作が変化した場合は、メディエーションを延長するだけで済みます。

    3. 管理を容易にする一元化されたインタラクション

欠点:

    1. 仲介者の責任は非常に重要かつ複雑です。

該当するシーン:

    複数のクラスが結合してネットワーク構造を形成する場合、ネットワーク構造をスター構造に分割する必要がある

コード:

#include<iostream>
#include<string>
#include<memory>
#include<vector>
class Mediator{
public:
    virtual void send(std::string message,Colleague* colleague) =0;
    virtual void add(Colleague* colleague)=0;
};

class Colleague{
public:
    Mediator* getMediator(){
        return mediator;
    }
    void setMediator(Mediator* const mediator){
        this->mediator=mediator;
    }
    Colleague(Mediator* mediator){
        this->mediator =mediator;
        this->mediator->add(this);
    }
    virtual void Notify(std::string message)=0;
private:
    Mediator* mediator;
};

class ConcreteColleague1:public Colleague{
public:
    ConcreteColleague1(Mediator* mediator):Colleague(mediator){}
    void send(std::string message){
        getMediator()->send(message,this);
    }
    void Notify(std::string message){
        std::cout<<"同事1收到信息:"+message<<std::endl;
    }
};

class ConcreteColleague2:public Colleague{
public:
    ConcreteColleague2(Mediator* mediator):Colleague(mediator){}
    void send(std::string message){
        getMediator()->send(message,this);
    }
    void Notify(std::string message){
        std::cout<<"同事2收到信息:"+message<<std::endl;
    }
};

class ConcreteMediator:public Mediator{
public:
    void add(Colleague* colleague){
        colleaguesList.push_back(colleague);
    }
    void send(std::string message,Colleague* colleague){
        for(auto value:colleaguesList){
            if(value!=colleague){
                value->Notify(message);
            }
        }
    }
private:
    std::vector<Colleague*> colleaguesList;
};


int
main(){

    Mediator* mediator =new ConcreteMediator();
    ConcreteColleague1* colleague1 =new ConcreteColleague1(mediator);
    ConcreteColleague2* colleague2 =new ConcreteColleague2(mediator);
    colleague1->send("早上好啊");
    colleague2->send("早安!");


    return 0;
}

メモモード:

        カプセル化を破壊することなく、オブジェクトの内部状態をキャプチャし、この状態をオブジェクトの外部に保存します。こうすることで、後でオブジェクトを元の状態に復元できます。

アドバンテージ:

    1. イニシエーター ロールのステータスが変更された場合、それは誤った変更である可能性がありますが、メモ モードを使用してこの誤った変更を復元できます。

    2. バックアップのステータスはイニシエータ ロールの外部に保存されるため、イニシエータ ロールは各バックアップのステータスを管理する必要がありません。

欠点:

    1. バックアップ オブジェクトに大量の情報が含まれている場合、または作成および回復操作が非常に頻繁に行われる場合、パフォーマンスに多大なオーバーヘッドが発生する可能性があります。

該当するシーン:

    後で必要になったときに現在の状態に復元できるように、ある時点のオブジェクトの状態を保存する必要があります。

    他のオブジェクトに現在のオブジェクトの状態を直接保存させると、オブジェクトの実装の詳細が公開され、オブジェクトのカプセル化が破壊されます。

実装コード:

#include<iostream>
#include<list>
#include<memory>
#include<string>
#include<vector>

class Memento{
public:
    explicit Memento(const std::string& state):state(state){}
    std::string getState()const{
        return state;
    }
private:
    std::string state;
};

class Originator{
public:
    std::string getState()const{
        return state;
    }
    void setState(const std::string& state){
        this->state=state;
    }
    Memento SaveStateMemento(){
        return Memento(state);
    }
    void getStateFromMemento(Memento memento){
        state=memento.getState();
    }

private:
    std::string state;
};

class Caretaker{
public:
    void add(Memento memento){
        mementoList.push_back(memento);
    }
    Memento get(int index){
        return mementoList[index];
    }

private:
    std::vector<Memento> mementoList;

};

int
main(){

    Originator originator;
    Caretaker caretaker;
    originator.setState("状态1,攻击力为100");

    //保存当前状态
    caretaker.add(originator.SaveStateMemento());

    //受到debuff,攻击力下降
    originator.setState("状态2,攻击力为80");

    //保存状态
    caretaker.add(originator.SaveStateMemento());

    std::cout<<"当前状态:"<<originator.getState()<<std::endl;
    std::cout<<"debuff状态结束,回到状态1"<<std::endl;
    originator.getStateFromMemento(caretaker.get(0));
    std::cout<<"恢复到状态1后的状态"<<originator.getState();

    return 0;
}

オブザーバーモード:

        オブジェクト間の 1 対多の依存関係を定義します。オブジェクトの状態が変化すると、それに依存するすべてのオブジェクトが通知され、自動的に更新されます。

アドバンテージ:

    1. 疎結合: オブザーバー パターンは疎結合設計を提供するため、オブジェクトの状態が変化したときに、他のオブジェクトがこの情報をどのように使用するかを知る必要がありません。はい、システムの拡張と保守が容易です

    2. 動的関連付け: オブザーバー パターンを使用すると、サブジェクトまたは他のオブザーバーのコードを変更することなく、実行時にオブザーバーを動的に追加または削除できます。

    3. 抽象的な分離: 主体と観察者は抽象的なインターフェイスを通じてのみ通信するため、両者の間の結合は具体的ではなく抽象的になります。

欠点:

    1. 予期しない更新が発生する可能性がある 通知を受け取った後にオブザーバーが何らかの操作を実行し、その操作によってトピックの状態が変化した場合、予期しない更新が発生する可能性があります。

    2. パフォーマンスの問題が発生する可能性がある 更新する必要があるオブザーバーが多数ある場合、すべてのオブザーバーに通知するとパフォーマンスの問題が発生する可能性があります。

    3. 複雑さが増す可能性がある 正しく実装されていない場合、オブザーバー パターンによってシステムの複雑さが増す可能性があります。

該当するシーン:

    オブジェクトの状態が変化した場合、他のオブジェクトに通知する必要があります。オブジェクトの状態が変化すると、いくつかの操作を実行する必要があります。複数のオブジェクト間で疎結合設計を実装する必要がある場合

コード:

#include<iostream>
#include<string>
#include<memory>
#include<list>

class AbstractHero{//抽象的英雄,抽象的观察者
public:
    virtual void Update() =0;
};

class HeroA :public AbstractHero{//具体的英雄,具体的观察者
public:
    HeroA(){
        std::cout<<"英雄A正在撸BOSS"<<std::endl;
    }
    virtual void Update(){
        std::cout<<"英雄A停止撸,待机状态"<<std::endl;
    }
};

class HeroB :public AbstractHero{
public:
    HeroB(){
        std::cout<<"英雄B正在撸BOSS"<<std::endl;
    }
    virtual void Update(){
        std::cout<<"英雄B停止撸,待机状态"<<std::endl;
    }
};

class AbstractBoss{//定义抽象的观察目标
public:
    virtual void addHero(AbstractHero* hero) =0;//添加观察者
    virtual void deleteHero(AbstractHero* hero)=0;//删除观察者
    virtual void notifv()=0;
};

class BOSSA:public AbstractBoss{
public:
    virtual void addHero(AbstractHero* hero){
        pHeroList.push_back(hero);
    }
    virtual void deleteHero(AbstractHero* hero){
        pHeroList.remove(hero);
    }
    virtual void notifv(){
        for(std::list<AbstractHero*>::iterator it=pHeroList.begin();it!=pHeroList.end();it++){
            (*it)->Update();
        }
    }
    std::list<AbstractHero*> pHeroList;
};


int
main(){

    //创建观察者
    AbstractHero* heroA=new HeroA;
    AbstractHero* heroB=new HeroB;

    //创建观察目标
    AbstractBoss* bossA=new BOSSA;
    bossA->addHero(heroA);
    bossA->addHero(heroB);

    std::cout<<"heroA 阵亡"<<std::endl;
    bossA->deleteHero(heroA);

    std::cout<<"Boss死了,通知其他英雄停止攻击"<<std::endl;
    bossA->notifv();
    delete heroA,heroB,bossA;

    return 0;
}

ステータスモード:

        オブジェクトの内部が変更されたときにオブジェクトの動作を変更できるようにし、オブジェクトがそのクラスを変更しているように見えます。それは戦略の延長として見ることができます。どちらも合成メカニズムに基づいています

        これらはすべて、作業の一部を「ヘルパー」オブジェクトに委任することによって、さまざまな状況で動作を変更します。このポリシーにより、これらのオブジェクトは互いに完全に独立し、オブジェクトの存在は認識されません。

        ただし、状態モデルは特定の状態間の依存関係を制限せず、さまざまなシナリオで状態を変更することを許可します。

アドバンテージ:

    1. 変換ルールをカプセル化し、可能な状態も列挙します。状態を列挙する前に、状態の種類を確認する必要があります。

    2. 開閉の原則に従ってください。既存の状態クラスやコンテキストを変更せずに、新しい状態を導入できる

    3. 肥大化したステートマシンの条件文を排除してコンテキストコードを簡素化する

欠点:

    1. ステート マシンの状態が少数しかない場合、またはほとんど変更されない場合、システムの複雑さが増加します。

    2. 「開閉原理」のサポートがあまり良くなく、状態を切り替えることができる状態モードの場合、新しい状態クラスを追加するには、変換を担当するソースコードを変更する必要があり、そうしないと、状態を切り替えることができなくなります。新しい状態。

該当するシーン:  

    1. オブジェクトの動作はその状態に依存し、実行時の状態に応じて動作を変更する必要があります。

    2. コードには、オブジェクトの状態に関連する多数の条件文が含まれています。

コード:

#include<iostream>
#include<string>
#include<memory>
class War;
class State{
public:
    virtual void Prophase(){}
    virtual void Metaphase(){}
    virtual void Anaphase(){}
    virtual void End(){}
    virtual void CurrentState(War* war){}
};

class War{
private:
    State* m_state;//目前状态
    int m_days;//战争持续时间
public:
    War(State* state):m_state(state),m_days(0){}
    ~War(){ delete m_state; }
    int GetDays(){ return m_days; }
    void SetDays(int days){ m_days = days; }
    void SetState(State* state){ delete m_state; m_state = state; }
    void GetState(){ m_state->CurrentState(this); }
};

//战争结束状态
class EndState: public State{
public:
    void End(War* war){
        std::cout<<"战争结束"<<std::endl;
    }
    void CurrentState(War* war){
        End(war);
    }
};

//后期
class AnaphaseState: public State{
public:
    void Anaphase(War* war){//后期具体的行为
        if(war->GetDays() < 30){
            std::cout<<"第"<<war->GetDays()<<"天:战争后期,双方拼死一搏"<<std::endl;
        }else{
            war->SetState(new EndState());
            war->GetState();
        }
    }
    void CurrentState(War* war){ Anaphase(war); }
};


int
main(){

    War* war=new War(new AnaphaseState());
    for(int i=1;i<50;i+=5){
        war->SetDays(i);
        war->GetState();
    }
    return 0;
}

戦略モード:

        アルゴリズムがカプセル化されている場合、問題を処理するときに複数のアルゴリズムが存在することになり、これはテンプレート モードと似ています。

アドバンテージ:

    1. 「開閉原理」を完全サポート ユーザーは元のコードを変更することなくアルゴリズムや動作を選択でき、新しいアルゴリズムや動作を柔軟に追加することもできます。

    2. 複数の条件判定の使用を回避し、プログラム保守の難易度を軽減します。

    3. 強力な拡張性

    4. アルゴリズム再利用メカニズムを提供し、さまざまな環境クラスがこれらの戦略クラスを簡単に再利用できます。

欠点:

    1. 戦略クラスの増加(クラス爆発)につながり、将来の維持が困難になります。

    2. すべてのポリシー クラスが外部に公開されており、クライアントの権限が大きすぎ、セキュリティが弱い

該当するシーン:

    1. システム内に多くのクラスがあり、それらの唯一の違いがそれらの動作である場合、戦略パターンを使用して、オブジェクトが多くの動作の中から動的に選択できるようにすることができます。

    2. システムは複数のアルゴリズムから 1 つを動的に選択する必要がある

    3. オブジェクトに多くの動作があり、適切なパターンがない場合は、複数の条件付き選択ステートメントを使用してこれらの動作を実装する必要があります。

予防:

    システムに 4 つを超える戦略がある場合は、戦略爆発の問題を解決するためにハイブリッド モードの使用を検討する必要があります。

コード:

#include<iostream>
#include<string>
#include<memory>

class WeaponStrategy{
public:
    virtual void UseWeapon()=0;
};

class Knife :public WeaponStrategy{
public:
    virtual void UseWeapon(){
        std::cout<<"使用匕首"<<std::endl;
    }
};

class AK47 :public WeaponStrategy{
public:
    virtual void UseWeapon(){
        std::cout<<"使用AK47"<<std::endl;
    }
};

class Character{
public:
    WeaponStrategy* pWeapon;
    void setWeapon(WeaponStrategy* pWeapon){
        this->pWeapon=pWeapon;
    }
    void ThrowWeapon(){
        this->pWeapon->UseWeapon();
    }
};

int
main(){

    Character* character=new Character;
    WeaponStrategy* knife=new Knife;
    WeaponStrategy* ak47=new AK47;
    character->setWeapon(ak47);
    character->ThrowWeapon();
    character->setWeapon(knife);
    character->ThrowWeapon();
    delete character,knife,ak47;
    return 0;
}

テンプレートモード:

        アルゴリズムのスケルトンを 1 回の操作で定義し、一部のステップをサブクラスに延期します。テンプレート メソッドを使用すると、アルゴリズムの構造を変更せずに、サブクラスでアルゴリズムの特定のステップを再定義できます。

アドバンテージ:

    1. 定数部分をカプセル化し、変数部分を拡張する

    2. パブリックコードを抽出してメンテナンスを容易にする

    3. 動作は親クラスによって制御され、サブクラスによって実装されます(コードの再利用を実現するため)

欠点:

    1. 実装ごとにサブクラスの実装が必要となるため、クラス数が増加し、システムが大型化する

該当するシーン:

        特定のビジネス ロジックでは、オブジェクトごとに詳細な実装が異なりますが、論理フレームワークは同じです

コード:

#include<iostream>
#include<algorithm>
#include<string>

class DrinkTemplate{//做饮料模板
public:
    virtual void BoildWater()=0;
    virtual void Brew()=0;
    virtual void PourInCup()=0;
    virtual void AddSomething()=0;
    void Make(){
        BoildWater();
        Brew();
        PourInCup();
        AddSomething();
    }
};

class Coffee :public DrinkTemplate{
    virtual void BoildWater(){
        std::cout<<"煮山泉水"<<std::endl;
    }
    virtual void Brew(){
        std::cout<<"冲泡咖啡"<<std::endl;
    }
    virtual void PourInCup(){
        std::cout<<"咖啡加入杯中"<<std::endl;
    }
    virtual void AddSomething(){
        std::cout<<"加糖加牛奶"<<std::endl;
    }
};

class Tea :public DrinkTemplate{
    virtual void BoildWater(){
        std::cout<<"煮自来水"<<std::endl;
    }
    virtual void Brew(){
        std::cout<<"冲泡铁观音"<<std::endl;
    }
    virtual void PourInCup(){
        std::cout<<"茶水加入杯中"<<std::endl;
    }
    virtual void AddSomething(){
        std::cout<<"加下雨天氛围"<<std::endl;
    }
};

int
main(){

    Tea* tea = new Tea;
    tea->Make();
    Coffee* coffee=new Coffee;
    coffee->Make();

    return 0;
}

ビジターモード:

        特定のデータ構造の各要素に作用するいくつかの操作をカプセル化し、データ構造を変更せずにこれらの要素に作用する新しい操作を定義できます。

アドバンテージ:

    1. 優れた拡張性。要素に対する操作を拡張するには、訪問者を追加するだけです

    2. 単一責任の原則を遵守します。関連する操作は訪問者にカプセル化され、訪問者は単一の操作を担当します。

    3. デカップリング、データ構造自体とそれに作用する操作を切り離す

欠点:

    1. カテゴリを追加するのは簡単ではありません。要素クラスが追加されるたびに、訪問者のインターフェイスと実装を変更する必要があります。

    2. 依存関係逆転の原則に違反し、訪問者は抽象的な要素ではなく特定の要素を提供します。

    3. カプセル化を破棄します。訪問者は訪問した要素の詳細を取得できます

該当するシーン:

        データ構造が比較的安定しており、アルゴリズムが変更しやすいシステムに適しています。

コード:

#include<iostream>
#include<algorithm>
#include<string>
#include<queue>
#include<List>
class Man;
class Woman;

class Action{
public:
    virtual void getManResult(Man* man) =0;
    virtual void getWomanResult(Woman* woman) =0;
};

class Success:public Action{
public:
    void getManResult(Man* man) override{
        std::cout<<"男人的给的评价该歌手是很成功"<<std::endl;
    }
    void getWomanResult(Woman* woman) override{
        std::cout<<"女人给的评价是该歌手很成功"<<std::endl;
    }
};

class Fail:public Action{
public:
    void getManResult(Man* man) override{
        std::cout<<"男人的给的评价该歌手是很失败"<<std::endl;
    }
    void getWomanResult(Woman* woman) override{
        std::cout<<"女人给的评价是该歌手很失败"<<std::endl;
    }
};

class Person{
public:
    virtual void accept(Action* action) =0;
};

class Man:public Person{
public:
    void accept(Action* action)override{
        action->getManResult(this);
    }
};

class Woman:public Person{
public:
    void accept(Action* action)override{
        action->getWomanResult(this);
    }
};

class ObjectStructure{
public:
    void attach(Person* p){
        persons.push_back(p);
    }
    void detach(Person* p){
        persons.remove(p);
        delete p;
    }
    //显示测评情况
    void display(Action* action){
        for(auto value:persons){
            value->accept(action);
        }
    }

private:
    std::list<Person*> persons;
};


int
main(){

    ObjectStructure* objectStructure =new ObjectStructure;
    objectStructure->attach(new Man);
    objectStructure->attach(new Woman);


    return 0;
}

参考:

C++デザインパターン(全23種類)_momoholaのブログ - CSDNブログ

C++_c++ デザイン パターンのいくつかの一般的なデザイン パターン_lTimej のブログ - CSDN ブログ

C++_c++ 設計モデルでよく使用される 11 の設計パターン_CBoy_JW のブログ - CSDN ブログ

ビッグトークデザインパターン

効果的なC++

"C++ デザイン パターン"_c++ デザイン パターン_Yiqou Ersanli のブログ-CSDN ブログ

txinyu のブログ_-CSDN ブログ

おすすめ

転載: blog.csdn.net/songbijian/article/details/132656594