Chain of Responsibility Design Pattern

ยท

3 min read

  • This design pattern is used to pass a request along a chain of handlers. Each handler decides whether to process the request or pass it to the next handler in the chain.

  • A chain of components that all get a chance to process a command or a query, optionally having default processing implementation and an ability to terminate the processing chain

  • For example, we have a class Creature in a video game, it has two attributes of interest with it an attack value and a defence value, both initialised alongside the name when the Creature is constructed

  • A create modifier enhances the capabilities of this creature, such as doubling its attack value or increasing its defence capacity as needed in higher levels of the same game

Now we can go on to create concrete modifiers

Explaining the chain traversal

  • We only call the base class handle from the main

  • The handler checks for the next and if it exists it calls handle() for the next modifier in the chain

  • As in our case the next modifier is a concrete modifier which has overridden the virtual function, it has two handle functions at its disposal, the one being the overridden one and the other being the original base class one

  • It uses the overridden handle to implement concrete functionality and then call the base class handle which calls the next handler

  • CreatureModifier::handle() => DoubleAttackModifier::handle() =>CreatureModifier::handle (the base for DoubleAttackModifier as its base has ref to IncreaseDefenseModifier) => IncreaseDefenseModifier::handle() => CreatureModifier::handle(); (the base for IncreaseDefenseModifier )

Breaking the Chain of Responsibelity

  • If we override and leave the concrete handler empty , no chain traversal will happen

Now none of the modifiers will have any effect.

CODE


#include <iostream>
#include<queue>
#include<utility>
#include<algorithm>
#include<climits>
#include<cmath>
#include<unordered_map>
#include<unordered_set>
#include<vector>
#include<stack>
#include<sstream>
#include<cstring>
#include<string>
#include <deque>
#include <map>
#include<queue>
#include<bitset>
#include<set>
#include<queue>
#include<cstring>
#include<string>
#include<algorithm>
#include <fstream>
#include <tuple>
using namespace std;
typedef long long  ll;
ll MOD =1e9+7;


struct Creature{
  string name;
  int attack, defense;

  Creature(const string& name, const int attack, const int defense)
    : name(name),
      attack(attack),
      defense(defense){}

  friend ostream& operator<<(ostream& os, const Creature& obj){
    return os
      << "name: " << obj.name
      << " attack: " << obj.attack
      << " defense: " << obj.defense;
  }
};

class CreatureModifier{
  CreatureModifier* next{ nullptr }; // unique_ptr
protected:
  Creature& creature; // pointer or shared_ptr
public:
  explicit CreatureModifier(Creature& creature)
    : creature(creature){}
  virtual ~CreatureModifier() = default;

  void add(CreatureModifier* cm){
    if (next) next->add(cm);
    else next = cm;
  }
  // two approaches:
  // 1. Always call base handle(). There could be additional logic here.
  // 2. Only call base handle() when you cannot handle things yourself.
  virtual void handle(){
    if (next) next->handle();
  }
};

class DoubleAttackModifier : public CreatureModifier{
public:
  explicit DoubleAttackModifier(Creature& creature)
    : CreatureModifier(creature)
  {}

  void handle() override {
    creature.attack *= 2;
    cout<<"DoubleAttackModifier"<<endl;
    CreatureModifier::handle();
  }
};

class IncreaseDefenseModifier : public CreatureModifier{
public:
  explicit IncreaseDefenseModifier(Creature& creature)
    : CreatureModifier(creature){}

  void handle() override {
    if (creature.attack <= 2)
      creature.defense += 2;
    cout<<"IncreaseDefenseModifier"<<endl;
    CreatureModifier::handle();
  }
};

class NoBonusesModifier : public CreatureModifier {
public:
  explicit NoBonusesModifier(Creature& creature)
    : CreatureModifier(creature)
  {}

  void handle() override{
    // nothing
  }
};

int main(){
  Creature goblin{ "Goblin", 1, 1 };
    // 1. Double the creature's attack
    // 2. Increase defense by 1 unless power > 2
    // 3. No futher bonuses can be applied to this creature
  CreatureModifier root{ goblin };

  NoBonusesModifier nb{ goblin }; // effectively Command objects
  DoubleAttackModifier r1{ goblin };
  IncreaseDefenseModifier r2{ goblin };


  root.add(&nb);
  root.add(&r1);
  root.add(&r2);
  // root->r1->r2
  root.handle(); // annoying

  cout << goblin << endl;

  //getchar();
  return 0;
}
ย