Design Pattern

Table of Contents

1 Creation

1.1 Abstract Factory

  • Implemented by Factory Method.
  • Focus on a family of products.
  • ConcreteFactory is often singleton
abstract class AbstractFactory {
  + CreateA()
  + CreateB()
}

class ConcreteFactory1 {
  + CreateA();
  + CreateB();
}

class ConcreteFactory2 {
  + CreateA();
  + CreateB();
}

abstract class AbstractA
abstract class AbstractB

class A1
class A2
class B1
class B2

AbstractFactory<..ConcreteFactory1
AbstractFactory<..ConcreteFactory2
AbstractA<..A1
AbstractA<..A2
AbstractB<..B1
AbstractB<..B2

ConcreteFactory1*--A1
ConcreteFactory1*--B1
ConcreteFactory2*--A2
ConcreteFactory2*--B2

The implementation:

class AbstractFactory {
  virtual CreateA() {}
  virtual CreateB() {}
};
class ConcreteFactory1 {
  virtual CreateA() {new A1;}
  virtual CreateB() {new B2;}
};
class ConcreteFactory2 {
  virtual CreateA() {new A2;}
  virtual CreateB() {new B1;}
};
// client code
ConcreteFactory1 factory;
AbstractA *a = factory.CreateA();
AbstractB *b = factory.CreateB();

1.2 Builder

1.2.1 Class Diagram

class Director {
}

Director <|-- Builder
note right of Director: for all objects in structure { builder->BuildPart();}
abstract class Builder {
  BuildPart();
}
class ConcreteBuilder {
  BuildPart();
  GetResult();
}

Builder <-- ConcreteBuilder
Product <.. ConcreteBuilder

1.2.2 Sequence diagram

participant Client
participant Director
participant ConcreteBuilder

Client --> ConcreteBuilder : new ConcreteBuilder
Client --> Director : new Director(concreteBuilder)
Client --> Director : Construct()
Director --> ConcreteBuilder : BuildPartA()
Director --> ConcreteBuilder : BuildPartB()
Director --> ConcreteBuilder : BuildPartC()
Director --> ConcreteBuilder : GetResult()

wikitmp_builder_sequence.png

1.2.3 Implementation

Director::Create() {
  builder.BuildPartA();
  builder.BuildPartB();
  builder.BuildPartC();
};
// client knows which concreteBuilder to use,
// so it knows which the product is
ConcreteBuilder builder;
director = Director(builder);
director.Create();
builder.GetResult();

2 Structure

2.1 Singleton

  class Singleton {
  public:
    static Singleton* Instance() {
      if (m_instance == 0) {
        m_instance = new Singleton;
      }
      return m_instance;
    }
  protected:
    Singleton();
  private:
    static Singleton* m_instance;
  };

  Singleton *Singleton::m_instance = 0;

3 Behavior Pattern

3.1 Visitor pattern

Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing theclasses of the elements on which it operates.

3.1.1 The problem and the naive solution

We have a list of classes in a class hierarchy, e.g. AST nodes. We want to define many operations, each operates different on different nodes.

 abstract class Node {
   {abstract} TypeCheck();
   {abstract} GenerateCode();
   {abstract} PrettyPrint();
 }

 class NumNode {
   TypeCheck();
   GenerateCode();
   PrettyPrint();
 }

 class ExpNode

 class AddExpNode {
   TypeCheck();
   GenerateCode();
   PrettyPrint();
 }

 Node <-- NumNode
 Node <-- ExpNode
 ExpNode <-- AddExpNode

This is not good. Application code is mixed with the AST node implementation.

3.1.2 The pattern

We define a hierarchy of visitors, and defines different behavior inside the visitors for different nodes they operate on. This is called double-dispatch: The virtual functions lookup is decided not only by the object the function is defined on, but also the arguments it accepts.

 abstract class Node {
   {abstract} Accept(Visitor v);
 }

 class NumNode {
   Accept(Visitor v);
 }
 note left: v.visit(this);

 class ExpNode

 class AddExpNode {
   Accept(Visitor v);
 }
 note left: v.visit(this);


 Node <-- NumNode
 Node <-- ExpNode
 ExpNode <-- AddExpNode

 abstract class Visitor {
   {abstract} visit(NumNode node);
   {abstract} visit(AddExpNode node);
 }

 class TypeCheckVisitor {
   visit(NumNode node);
   visit(AddExpNode node);
 }

 class GenerateCodeVisitor {
   visit(NumNode node);
   visit(AddExpNode node);
 }

 class PrettyPrintVisitor {
   visit(NumNode node);
   visit(AddExpNode node);
 }

 Visitor <-- TypeCheckVisitor
 Visitor <-- GenerateCodeVisitor
 Visitor <-- PrettyPrintVisitor

3.1.3 Example

   public interface AST {
       public static abstract class ASTNode {
           public abstract Object accept(Visitor visitor);
       }
       public static abstract class Exp extends ASTNode {

       }
       public static class NumExp extends Exp {
           double _val;
           public Object accept(Visitor visitor) {
               return visitor.visit(this);
           }
       }
       public static class AddExp extends CompoundArithExp {
           public AddExp(List<Exp> args) {
               super(args);
           }
           public Object accept(Visitor visitor) {
               return visitor.visit(this);
           }
       }
   }
   public class Evaluator implements Visitor<Value> {
       public Value visit(NumExp e) {
           return new NumVal(e.v());
       }
       public Value visit(AddExp e) {
           List<Exp> operands = e.all();
           double result = 0;
           for (Exp exp : operands) {
               NumVal intermediate = (NumVal) exp.accept(this); // Dynamic
               // type-checking
               result += intermediate.v(); // Semantics of AddExp in terms of the
               // target language.
           }
           return new NumVal(result);
       }
   }