C++責任連鎖+戦略モード、連鎖処理処理を実現

商品の加工には複数のプロセスを経る必要があると想像してください。このプロセスは、最初から最後まで続く責任の連鎖のようなものです。これは、ネットワークが送信しているときのようなもので、メッセージはピア ホストに送信され、プロトコル スタックを通過します。このプロセスでは、メッセージはさまざまなプロトコル スタック レベルで層ごとに取り除かれ、IP ヘッダーが取り除かれ、TCP ヘッダーが取り除かれます。そして http ヘッダーを切り離します。それはまた、美しい踊り子が琵琶を持たなくなり、ゆっくりとベールを上げていくようなもので、ちょうど李青照の「梅の花」の「軽く服を解き、一人で蘭船に行く」のように、最終的には反対の層が得られます。彼女が望むこと。

(注:純粋な知識の概要共有、事例、事業内容は架空のものです)

序文

一般的なアプリケーション シナリオを想定します。企業アプリケーションのワークフローでは、アプリケーションは、アプリケーションの最初の送信からマネージャー、そして最終的に最終部門のタスク受信者に一連の流れで渡されます。これらの各オブジェクトは承認され、次のステップに転送できます。責任の連鎖のようなものです。同時に、要件の変化に応じて新しいプロセッサの変更や追加が必要になる場合があるため、このような戦略パターン + 責任チェーンを採用することは非常に適切です。

Chain of Responsibility パターンは、ポリシー オブジェクトを使用した「再帰の動的一般化」と見なすことができ、呼び出しが行われると、チェーン シーケンス内の各ポリシーが呼び出しを満たすよう試みます。このプロセスは、ポリシーが呼び出しを正常に満たすか、シーケンスの終わりに到達するまで終了しません。ポリシー チェーンをたどることにより、特定の終了条件に達して最終処理に到達するまで、関数のさまざまな実装が呼び出されます。

戦略パターン

ストラテジー モード (Strategy): アルゴリズムのファミリーを定義し、相互に置き換えることができるように個別にカプセル化します。このモードでは、アルゴリズムの変更がアルゴリズムを使用する顧客に影響を与えないようにすることができます。ストラテジは、いくつかのアルゴリズム識別子を定義する、つまり、いくつかの抽象メソッドを定義するインターフェイスです。

Strategy パターンでは、一連のアルゴリズムを定義し、それらを共通のインターフェイスを実装する個別のクラスにカプセル化します。その後、実行時に具体的なアルゴリズム クラスを選択して、特定の動作を実行できます。

ストラテジー モードの主な利点は次のとおりです。

1. 継承に代わる手法を提供し、継承の利点(コードの再利用)を維持するだけでなく、継承よりも柔軟です(アルゴリズムが独立しており、任意に拡張できます)。

2. プログラム内で複数の条件付き転送ステートメントを使用することを避け、システムをより柔軟で拡張しやすくします。

3. ほとんどの GRASP 原則と共通の設計原則、高い凝集力と低い結合度に準拠します。

戦略パターンを使用した例を次に示します。

#include <iostream>

// 策略接口
class Strategy {
public:
    virtual void execute() = 0;
};

// 具体策略类1
class ConcreteStrategy1 : public Strategy {
public:
    void execute() override {
        std::cout << "Executing strategy 1" << std::endl;
    }
};

// 具体策略类2
class ConcreteStrategy2 : public Strategy {
public:
    void execute() override {
        std::cout << "Executing strategy 2" << std::endl;
    }
};

// 上下文类
class Context {
private:
    Strategy* strategy;

public:
    Context(Strategy* strategy) : strategy(strategy) {}

    void setStrategy(Strategy* strategy) {
        this->strategy = strategy;
    }

    void executeStrategy() {
        strategy->execute();
    }
};

int main() {
    // 创建具体策略对象
    ConcreteStrategy1 strategy1;
    ConcreteStrategy2 strategy2;

    // 创建上下文对象并设置初始策略
    Context context(&strategy1);

    // 执行初始策略
    context.executeStrategy();

    // 切换策略并执行
    context.setStrategy(&strategy2);
    context.executeStrategy();

    return 0;
}

上の例では、`execute()` メソッドを持つ `Strategy` インターフェイスを定義しました。次に、それぞれ `execute()` メソッドを実装する 2 つの具体的な戦略クラス `ConcreteStrategy1` と `ConcreteStrategy2` を作成しました。

次に、コンテキスト クラス `Context` を定義します。これには、`Strategy` インターフェイスを指すポインタ メンバー変数があります。コンストラクターでは、初期ストラテジ オブジェクトを渡し、`executeStrategy()` メソッドでストラテジ オブジェクトの `execute()` メソッドを呼び出します。

`main()` 関数では、具体的なポリシー オブジェクトを作成し、それをコンテキスト オブジェクトに渡します。次に、コンテキスト オブジェクトの `executeStrategy()` メソッドを呼び出して、ストラテジーを実行します。

戦略パターンを使用すると、実行時にさまざまな戦略を動的に選択でき、新しい戦略クラスを簡単に追加できます。これにより、コードがより柔軟で拡張可能になります。

責任連鎖パターン

GOF の「Design Patterns: The Foundation of Reusable Object-Oriented Software」では、責任連鎖パターンについて次のように述べられています。複数のオブジェクトにリクエストを処理する機会を与え、それによってリクエストの送信者と受信者の間の競合を回避します。カップリング関係。これらのオブジェクトは、1 つのオブジェクトが処理するまで連鎖します。Chain of Responsibility パターンでは、各ハンドラーには次のハンドラーへの参照が含まれており、リクエストが到着すると、最初のハンドラーによって最初に処理され、そのハンドラーがリクエストを処理できる場合は処理が終了し、それ以外の場合はリクエストをハンドラーに渡します。リクエストが処理されるか、チェーンの終わりに到達するまで、次のハンドラーを実行します。

長所と短所

1. 結合の度合いを減らします。責任連鎖パターンにより、オブジェクトはどのオブジェクトがそのリクエストを処理するかを知る必要がなくなります。オブジェクトは、リクエストが正しく処理されることを認識するだけで済みます。受信者も送信者も相手に関する明確な情報を持っておらず、チェーン内のオブジェクトはチェーンの構造を知る必要はありません。

2. オブジェクトへの責任の割り当ての柔軟性が強化されました。オブジェクト間で責任を割り当てる場合、責任チェーンにより柔軟性が高まります。実行時にチェーンを動的に追加または変更することで、リクエストを処理する責任を追加または変更できます。


3. 受け入れは保証されません: リクエストには明確な受信者がないため、処理されるという保証はなく、チェーンの最後までリクエストが処理されない可能性があります。チェーンが適切に構成されていないために、リクエストが処理されない場合もあります。

以下は、単純な C++ 責任連鎖パターンの例です。

#include <iostream>
using namespace std;

// 抽象处理者
class Handler {
public:
    virtual void setNext(Handler* next) = 0;
    virtual void handleRequest(int request) = 0;
};

// 具体处理者A
class ConcreteHandlerA : public Handler {
private:
    Handler* next;
public:
    void setNext(Handler* next) {
        this->next = next;
    }
    void handleRequest(int request) {
        if (request >= 0 && request < 10) {
            cout << "ConcreteHandlerA处理请求:" << request << endl;
        }
        else if (next != nullptr) {
            next->handleRequest(request);
        }
    }
};

// 具体处理者B
class ConcreteHandlerB : public Handler {
private:
    Handler* next;
public:
    void setNext(Handler* next) {
        this->next = next;
    }
    void handleRequest(int request) {
        if (request >= 10 && request < 20) {
            cout << "ConcreteHandlerB处理请求:" << request << endl;
        }
        else if (next != nullptr) {
            next->handleRequest(request);
        }
    }
};

// 具体处理者C
class ConcreteHandlerC : public Handler {
private:
    Handler* next;
public:
    void setNext(Handler* next) {
        this->next = next;
    }
    void handleRequest(int request) {
        if (request >= 20 && request < 30) {
            cout << "ConcreteHandlerC处理请求:" << request << endl;
        }
        else if (next != nullptr) {
            next->handleRequest(request);
        }
    }
};

int main() {
    // 创建处理者对象
    Handler* handlerA = new ConcreteHandlerA();
    Handler* handlerB = new ConcreteHandlerB();
    Handler* handlerC = new ConcreteHandlerC();

    // 设置处理者的下一个处理者
    handlerA->setNext(handlerB);
    handlerB->setNext(handlerC);

    // 发起请求
    handlerA->handleRequest(5);
    handlerA->handleRequest(15);
    handlerA->handleRequest(25);

    // 释放内存
    delete handlerA;
    delete handlerB;
    delete handlerC;

    return 0;
}

具体的な実装例

機械のガイドレールにあるスライダーの制御を例にとると、スライダーを要求通りに正確に動かすには、パラメーターの補正、速度制限、アルゴリズムの補正などの一連の処理を経る必要があります。そして最後にそれをコントローラーに送信し、デバイスの応答を返します。優れた設計がなければ、新しい機能が追加されるたびに、それを適切に維持し、拡張することができません。そして、責任の連鎖と戦略パターンを使用することは、それに対処する良い方法です。例えば、スライダーの動きは工場の組立ラインのように一連の処理(責任連鎖モード)を経ることができます。

まず、スライダーの基本的なアクション関数戦略インターフェイスを定義します。

class SliderIFA
{
public:
  SliderIFA() = default;
  virtual ~SliderIFA() = default;

public:
  // 按指定位置移动
  virtual void tMove() = 0;
  // 按步进方式移动
  virtual void iMove() = 0;
  virtual void enable() = 0;
  // ......
}

次に、ベーシックスキル、スローモーションスキル、ファストモーションスキル、ファンシーモーションスキルなどの異なるスキル(戦略)を、戦略モードと責任連鎖モードを組み合わせて実装し、別々に実装することで同一の処理を実現します。戦略的な行動。

ストラテジ インターフェイス SliderIFA から継承した親クラス BaseIFA を定義し、最初にストラテジをパブリック クラスとして実装し、責任連鎖モデルを内部的に実装します。また、このような親クラスを定義することのもう 1 つの利点は、多くのストラテジ インターフェイスがある場合、親クラスから継承されたストラテジ クラスはすべてのストラテジ インターフェイスを実装する必要がなく、必要なインターフェイスをオーバーロードするだけで済むことです。他の非オーバーロード インターフェイスは、親クラスのメソッドを自動的に実行します。 

class BaseIFA : public SliderIFA
{
public:
  BaseIFA() {}

public:
  void setNextStrategy(std::shared_ptr<SliderIFA> coreIfa) { nextStrategy_ = std::move(coreIfa); }

public:
  void tMove() override;
  void iMove() override;
  void enable() override;

protected:
 std::shared_ptr<SliderIFA> nextStrategy_;

}
void BaseIFA::enable()
{
  if (nextStrategy_)
  {
    return nextStrategy_->enable();
  }
}

void BaseIFA::tMove()
{
  if (nextStrategy_)
  {
    return nextStrategy_->tMove();
  }
}

void BaseIFA::iMove()
{
  if (nextStrategy_)
  {
    return nextStrategy_->iMove();
  }
}

たとえば、責任者 1 の実装は次のようになります。

class Hander1:public BaseIFA
{
public:
  Hander1() {}

public:
  void tMove() override;
  void iMove() override;
  void enable() override;
}

void Hander1::enable()
{
  if (nextStrategy_)
  {
    return nextStrategy_->enable();
  }
}

void Hander1::tMove()
{
  if (nextStrategy_)
  {
    return nextStrategy_->tMove();
  }
}

void Hander1::iMove()
{
  if (nextStrategy_)
  {
    return nextStrategy_->iMove();
  }
}

責任者の実現2:

class Hander2:public BaseIFA
{
public:
  Hander2() {}

public:
  void tMove() override;
  void iMove() override;
  void enable() override;
}

void Hander2::enable()
{
  if (nextStrategy_)
  {
    return nextStrategy_->enable();
  }
}

void Hander2::tMove()
{
  if (nextStrategy_)
  {
    return nextStrategy_->tMove();
  }
}

void Hander2::iMove()
{
  if (nextStrategy_)
  {
    return nextStrategy_->iMove();
  }
}

最終的な使用法は次のとおりです。各ハンドラーはポリシー インターフェイスの実装であり、責任連鎖モデルに従って連鎖されます。例えば、3→1→2の処理順序で処理してもよいし、1→2→3の処理順序で処理してもよい。または、次の 2 つの処理ステップ 1 と 2 だけを実行して効果を実現します。

int main()
{
    std::shared_ptr<SliderIFA> core_ = nullptr;

    auto hander1_ = std::make_shared<Hander1>();
    auto hander2_ = std::make_shared<Hander2>();
    auto hander3_ = std::make_shared<Hander3>();

    // 举例,实现1->2->3的排列组合链式处理

    hander1_->setNextStrategy(hander2_ );
    hander2_->setNextStrategy(hander3_ );

    core_ = std::move(hander1_); //入口

    // 执行
    core_->tMove();

    return 0;
} 

最後の tMove インターフェイスは、最初に hander1->hander2->hander3 のチェーン処理を経て、目的の効果を実現します。また、任意に配置・組み合わせることもでき、hander1→hander3なども可能です。

最適化と改善: BaseIFA 基本クラスを簡潔かつ直観的に保つために、デフォルトのポリシー クラスを最終的な責任連鎖の最後のレベルとして個別に統合できます。インターフェイスのデフォルトの実装はここに書かれています。この方法では、ストラテジー インターフェイス SliderIFA から継承した DefaultHander を定義し、その参照を BaseIFA 基本クラスで保持するだけで済みます。好き:

class BaseIFA : public SliderIFA
{
public:
  explicit BaseIFA(std::shared_ptr<DefaultHander> coreImp) : coreImp_(std::move(coreImp)) {}

public:
  void setNextStrategy(std::shared_ptr<SliderIFA> coreIfa) { nextStrategy_ = std::move(coreIfa); }

public:
  void tMove() override;
  void iMove() override;
  void enable() override;

protected:
 std::shared_ptr<SliderIFA> nextStrategy_;

 std::shared_ptr<DefaultHander> coreImp_;
}

void BaseIFA::enable()
{
  if (nextStrategy_)
  {
    return nextStrategy_->enable();
  }
  if (core_)
  {
    return core_->enable();
  }
}
 
void BaseIFA::tMove()
{
  if (nextStrategy_)
  {
    return nextStrategy_->tMove();
  }
  if (core_)
  {
    return core_->tMove();
  }
}
 
void BaseIFA::iMove()
{
  if (nextStrategy_)
  {
    return nextStrategy_->iMove();
  }
  if (core_)
  {
    return core_->iMove();
  }
}

class DefalutHander: public SliderIFA
{
public:
  BaseIFA() {}
 
public:
  void tMove() override;
  void iMove() override;
  void enable() override;

}

class Hander1:public BaseIFA
{
public:
  explicit Hander1(std::shared_ptr<DefaultHander> core_) {}

public:
  void tMove() override;
  void iMove() override;
  void enable() override;
}

void Hander1::enable()
{
  if (nextStrategy_)
  {
    return nextStrategy_->enable();
  }
  if (core_)
  {
    return core_->enable();
  }
}

void Hander1::tMove()
{
  if (nextStrategy_)
  {
    return nextStrategy_->tMove();
  }
  if (core_)
  {
    return core_->tMove();
  }
}

void Hander1::iMove()
{
  if (nextStrategy_)
  {
    return nextStrategy_->iMove();
  }
  if (core_)
  {
    return core_->iMove();
  }
}

int main()
{
    std::shared_ptr<SliderIFA> core_ = nullptr;

    //默认策略类
    auto default_ = std::make_shared<DefaultHander>();
    //策略实现一
    auto hander1_ = std::make_shared<Hander1>(default_ );
    //策略实现二
    auto hander2_ = std::make_shared<Hander2>(default_ );
    //策略实现三
    auto hander3_ = std::make_shared<Hander3>(default_ );

    // 举例,实现1->2->3的排列组合链式处理

    hander1_->setNextStrategy(hander2_ );
    hander2_->setNextStrategy(hander3_ );

    core_ = std::move(hander1_); //入口

    // 执行
    core_->tMove();

    return 0;
} 

その他のリソース

C++戦略パターン

C++ の一般的なデザイン パターン

パターンと原則 [オリジナル] - Justin - 博客园

デザインパターンエッセイシリーズ: アヒル-戦略パターン (戦略) [オリジナル] - Justin - 博客园

C++ ハンド テアリング コード (6) デザイン パターン: オブザーバー、戦略、責任の連鎖、デコレーター - 知識

Java デザイン パターン - 戦略パターン

C++ デザイン パターン - 責任連鎖モード

C++ デザイン パターン - 戦略パターンの基本概要_IT1995 のブログ-CSDN ブログ_c++ 戦略パターン

デザイン パターン (C++ 実装) (21) - 責任の連鎖パターン - huashuolin001 のブログ - CSDN ブログ

C++ デザイン パターン (7) 責任連鎖パターン: 一連の戦略パターンを採用してみる - プログラミングを楽しむだけ - C++ ブログ

おすすめ

転載: blog.csdn.net/qq8864/article/details/128849259