Modern C++ Design Pattern/Chatper 13. 책임사슬(Chain of Responsibility)

created : 2020-04-18T13:14:11+00:00
modified : 2020-09-26T14:22:30+00:00

cpp design pattern chain of responsibility

시나리오

struct Creature
{
  string name;
  int attack, defense;
  // Constructor, operator...
};

포인터 사슬

class CreatureModifier
{
  CreatureModifier* next{nullptr};
protected:
  Creature& creture; // reference, pointer or shared_ptr
public:
  explicit CreatureModifier(Creature& creature)
    : creature(creture) {}

  void add(CreatureModifier* cm)
  {
    if (next) next->add(cm);
    else next =cm;
  }

  virtual void handle()
  {
    if (next) next->handle();
  }
};
  • 참조를 넘겨받아 저장하고 변경할 준비
  • 추상 클래스가 아님
  • next는 다음 변경 작업을 가리킴
  • add()를 통해서 작업 사슬에 연결하여 추가
  • handle() 맴버 함수는 단순히 다음 항목을 처리한다. 원한다면 오버라이딩해서 처리한다.
    class DoubleAttackModifier : public CreatureModifier
    {
    public:
    explicit DoubleAttackModifier(Creature& creature)
      : CreatureModifier(creature) {}
    
    void handle() override
    {
      creature.attack *= 2;
      CreatureModifier::handle();
    }
    };
    
    class NoBonusesModifier : public CreatureModifier
    {
    public:
    explicit NoBonusesModifer(Creature& creature)
      : CreatureModifier(creature) {}
    
    void handle() override
    {
      // Do Nothing
    }
    };
    

    브로커 사슬

    ```cpp struct Game { signal<void(Query&)> queries; // Boost.Signals2 };

struct Query { string creature_name; enum Argumnet { attack, defense } argument; int result; };

class Creature { Game& game; int attack, defense; public: string name; Creature(Game& game, …) : game{game}, … { … } // Other Members };

int Creature::get_attack() const { Query q{ name, Query::Argument::attack, attack }; game.queries(q); return q.result; }

class CreatureModifier { Game& game; Creature& creature; public: CreatureModifier(Game& game, Creature& creature) : game(game), creature(creature) {} };

class DoubleAttackModifier : public CreatureModifier { connection conn; public: DoubleAttackModifier(Game& game, Creature& creature) : CreatureModifier(game, creature) { conn = game.queries.connect(& { if (q.creature_name == creature.name && q.argument == Query::Argument::attack) q.result *= 2; }) }

~DoubleAttackModifier() { conn.disconnect(); } } ```

요약

  • 컴포넌트들이 어떤 명령이나 조회 작업을 차례로 처리할 수 있게 하는 매우 단순한 디자인 패턴이다.
  • 이론적으로 포인터 사슬은 보통 vectorlist로 대체될 수 있다.
  • 좀 더 복잡한 브로커 사슬의 구현에서는 매개자 패턴과 관찰자 패턴을 활용한다.