3 types: creational, structural, behavioral

Creational

object creation, keeping flexibility and reusability…

Structural

assemble classes into bigger structure while keeping them flexible…

Behavioral

concerns algorithms and assigning responsibility, keeping the implementation not too coupled with the structure…

Mediator

decoupling between objects by preventing direct method calls and forcing doing so via a mediator object

Strategy

allow modify the “algorithm” to be run in runtime

class HandlerStrategy {
  virtual void doWork(...) = 0;
};

class Strat1: public HandlerStrategy {
  void doWork(...) override { ... }
}

class Strat2: public HandlerStrategy {
  void doWork(...) override { ... }
}

class Handler {
  void process(...) {
    strat_.doWork(...);
  }
  
  void setStrategy(HandlerStrategy strat) {
    strat_ = strat;
  }

  HandlerStrategy strat_;
};

int main() {
  Strat1 a;
  Strat2 b;
  
  // easy change strategy
  Handler handler;
  handler.setStrategy(a);
  handler.doWork(...);
  handler.setStrategy(b);
  handler.doWork(...);
}

Visitor

use frequently in parser/interpreter/…

allow separate between the algorithm and “object structure” it operates on by using double dispatch

single dispatch vs double dispatch

to separate the algorithm and structure, the idea is to create a separate class called Visitor:

struct Order;
struct NewOrder: public Order;
struct CancelOrder: public Order;

struct Visitor {
  virtual void visit(const NewOrder& o) = 0;
  virtual void visit(const CancelOrder& o) = 0;
};

struct VisitorImpl: public Visitor { ... } // implement visit() methods

int main() {
  Order *o = new NewOrder();
  Visitor *v = new VisitorImpl();
  /* 1 dispatch here */
  v->visit(*o); // this raises error!
  // because o is of type "Order" -> it looks for visit(Order o) method!
  // compiler isn't aware that o is actually type NewOrder (type erasure!)
}

to circumvent type erasure, we need another dispatch by adding an accept() method in each Order class: