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

created : Sat, 18 Apr 2020 22:14:11 +0900
modified : Sat, 26 Sep 2020 23:22:30 +0900
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();
  }
};
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
  }
};

브로커 사슬

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([&](Query& q) {
      if (q.creature_name == creature.name &&
        q.argument == Query::Argument::attack)
        q.result *= 2;
    })
  }

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

요약