Basic Design Patterns in C++

Larry | Peng Yang
Computer Science Fundamentals
23 min readSep 23, 2019

Creational Patterns

  • Builder
  • Factory
  • Factory Method
  • Abstract Factory
  • Prototype
  • Singleton

Structural Patterns

  • Adapter
  • Bridge
  • Composite
  • Decorator
  • Facade
  • Flyweight
  • Proxy
  • Curiously Recurring Template (Skip)
  • Interface-based Programming (IBP) (Skip)

Behavioral Patterns

  • Chain of Responsibility
  • Command
  • Interpreter
  • Iterator
  • Mediator
  • Memento
  • Observer
  • State
  • Strategy
  • Template Method
  • Visitor
  • Model-View-Controller (MVC) (Skip)

UML Notations

Visibility

+Public-Private#Protected~Package

Relationships

  • Association: associations are a relationship between two classes at the class level. That is, one class keeps a direct or indirect “link” to the associated class as a member.
between two classes
QueryBuilder is owned by SearchService
class Doctor
{
private:
vector<Patient *> m_patient{};
public:
Doctor();
void addPatient(Patient *pat);
};

class Patient
{
private:
vector<Doctor *> m_doctor{}; // so that we can use it here

// make addDoctor private because we don't want the public to use it.
// They should use Doctor::addPatient() instead, which is publicly exposed
void addDoctor(Doctor *doc)
{
m_doctor.push_back(doc);
}

public:
Patient();
// We'll friend Doctor::addPatient() so it can access the private function Patient::addDoctor()
friend void Doctor::addPatient(Patient *pat);
};
  • Inheritance
the superclass Person and the two subclasses Student and Professor
class Person
{
public:
private:
};
class Student: public Person
{
...
};
class Professor: public Person
{
...
};
  • Realization/Implementation
Interface SiteSearch is realized (implemented) by SearchService
class Shape   // An interface class only contains pure virtual functions
{
public:
virtual ~shape();
virtual void move_x(int x) = 0;
virtual void move_y(int y) = 0;
virtual void draw() = 0;
};
class Line : public Shape
{
public:
virtual ~line();
virtual void move_x(int x); // implements move_x
virtual void move_y(int y); // implements move_y
virtual void draw(); // implements draw
private:
point end_point_1, end_point_2;
};
  • Dependency: Dependencies typically are not represented at the class level — that is, the object being depended on is not linked as a member. Rather, the object being depended on is typically instantiated as needed. e.g. Our classes that use std::cout to accomplish the task of printing something to the console.
A Car uses/depends on Wheel
class Point
{
private:
double m_x, m_y;

public:
Point();
friend std::ostream& operator<< (std::ostream &out, const Point &point);
};

std::ostream& operator<< (std::ostream &out, const Point &point)
{
// Since operator<< is a friend of the Point class, we can access Point's members directly.
out << "Point(" << point.m_x << ", " << point.m_y;
return out;
}
  • Aggregation
    1. When representing a software or database relationship, e.g. car model engine ENG01 is part of a car model CM01, as the engine, ENG01, may be also part of a different car model.
    2. When the container is destroyed, the contents are usually not destroyed, e.g. a professor has students, when the professor dies the students don’t die along with him or her.
a Professor has 1 or more class to teach
class Class
{
private:
string m_name;
public:
Class(string name)
: m_name(name)
{}
string getName() { return m_name; }
};

class Professor
{
private:
Class *m_class; // holds only one class for simplicity, but it could teach many classes
public:
Professor(Class *class = nullptr)
: m_class(class)
{}
};
  • The Composition is a strong aggregation.
    1. It is a binary association
    2. It is a whole/part relationship
    3. A part could be included in at most one composite (whole) at a time
    4. If a composite (whole) is deleted, all of its composite parts are “normally” deleted with it.
Hospital has 1 or more Departments, and
each Department belongs to exactly one Hospital.
If Hospital is closed, so are all of its Departments.
class Department
{
private:
string name;
public:
Department(){}
};
class Hospital{
public:
Hospital(){}
private:
vector<Department*> m_departments;
};

1. Creational Patterns

1.1 Builder

Definition: This pattern allows a client object to construct a complex object by specifying only its type and content, being shielded from the details related to the object’s representation. This way the construction process can be used to create different representations.

Problem: We want to construct a complex object, however, we do not want to have a complex constructor member or one that would need many arguments. So we define an instance for creating an object but letting subclasses decide which class to instantiate and refer to the newly created object through a common interface.

Solution: Define an intermediate object whose member functions define the desired object part by part before the object is available to the client. Builder Pattern lets us defer the construction of the object until all the options for creation have been specified.

Example: C++ Code

  • Pizza: Object we want to build
  • PizzaBuilder: HawalianPizzaBuilder, SpicyPizzaBuilder, unique_ptr<Pizza> m_pizza;
  • Cook (also called Director): PizzaBuilder* m_pizzaBuilder;
  • Client: Use cook to create and open pizza, HawaiianPizzaBuilder hawaiianPizzaBuilder; cook.createPizza(&hawaiianPizzaBuilder);

1.2 Factory

Definition: A utility class that creates an instance of a class from a family of derived classes. The problem here is that once we add a new concrete product call we should modify the Factory class. It is not very flexible and it violates the open-close principle.

Problem: creates objects without exposing the instantiation logic to the client. refers to the newly created object through a common interface

Solution: parameterized Factories

Example: C++ Code

class ProductFactory{
public:
Product createProduct(string productID){
if (productID==ID1)
return new OneProduct();
if (productID==ID2) return
return new AnotherProduct();
... // so on for the other Ids

return null; //if the productID doesn't have any of the expected values
}
...
}

1.3 Factory Method

Definition: It defines an interface for creating an object, but leaves the choice of its type to the subclasses, creation being deferred at run-time. It refers to the newly created object through a common interface. Factory Method is similar to Abstract Factory but without the emphasis on families.

Problem: A framework needs to standardize the architectural model for a range of applications, but allow for individual applications to define their own domain objects and provide for their instantiation.

Solution:

Participants:

  • Product: Defines the interface for the type of objects the factory method creates
  • ConcreteProduct: Implements the Product interface
  • Factory: Declares the factory method, which returns an object of type Product
  • ConcreteFactory: Overrides the factory method to return an instance of a ConcreteProduct

Example: C++ Code

  • Document (Product): MyDocument (ConcreteProduct)
  • Application (Factory): MyApplication(ConcreteFactory), Document *_docs[10]; Document *CreateDocument(char *fn);
  • Client (main function): Use MyApplication, MyApplication myApp; myApp.NewDocument(“foo”);

1.4 Abstract Factory

Definition: A utility class that creates an instance of several families of classes. It can also return a factory for a certain group. The purpose of the Abstract Factory is to provide an interface for creating families of related objects, without specifying concrete classes. The Abstract Factory pattern is very similar to the Factory Method pattern. The main difference between the two is that with the Abstract Factory pattern, a class delegates the responsibility of object instantiation to another object via composition whereas the Factory Method pattern uses inheritance and relies on a subclass to handle the desired object instantiation.

Problem: We want to decide at run time what object is to be created based on some configuration or application parameter. When we write the code, we do not know what class should be instantiated.

Solution: Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

Participates:

  • AbstractFactory: Declares an interface for operations that create abstract product objects
  • ConcreteFactory: Implements the operations to create concrete product objects
  • AbstractProduct: Declares an interface for a type of product object
  • ConcreteProduct: a. Defines a product object to be created by the corresponding concrete factory, b. Implements the AbstractProduct interface
  • Client: Uses only interfaces declared by AbstractFactory and AbstractProduct classes

Example: C++ Code

  • Shape (AbstructProduct): Circle, Square, Ellipse, Rectangle
  • Factory (AbstructFactory): SimpleShapeFactory (create Circle and Square, RobustShapeFactory (create Ellipse and Rectangle)
  • Client (main function): Use Factory and Shape. Shape* shapes[3]; shapes[0] = factory->createCurvedInstance();

Difference between Factory Method and Abstract Factory

  • Factory Method is used to create one product only but Abstract Factory is about creating families of related or dependent products.
  • Factory Method depends on inheritance to decide which product to be created, while Abstract Factory, there’s a separate class dedicated to create a family of related/dependent Products and its (any concrete subclass factory) object can be passed to the client which uses it (composition).
  • Factory Method is just a method while Abstract Factory is an object.
  • Abstract Factory is one level higher in abstraction than Factory Method. Factory Method abstracts the way objects are created, while Abstract Factory also abstracts the way factories are created which in turn abstracts the way objects are created.
  • As Abstract Factory is at a higher level in abstraction, it often uses Factory Method to create the products in factories.

1.5 Prototype

Definition: A prototype pattern is used when the type of objects to create is determined by a prototypical instance, which is cloned to produce new objects.

Problem: This pattern is used, for example, when the inherent cost of creating a new object in the standard way (e.g., using the new keyword) is prohibitively expensive for a given application.

Solution: Declare an abstract base class that specifies a pure virtual clone() method. Any class that needs a "polymorphic constructor" capability derives itself from the abstract base class and implements the clone() operation.

Participants:

  • Client — creates a new object by asking a prototype to clone itself.
  • Prototype — declares an interface for cloning itself.
  • ConcretePrototype — implements the operation for cloning itself.

Example: C++ Code

  1. Record (base Prototype): CarRecord, BikeRecord, PersonRecord
  2. RecordFactory: Keep records for each concrete prototype and call their clone function when createRecord is called.
  3. Client (main function): Use RecordFactory to create/get records.

1.6 Singleton

Definition: It ensures that a class has only one instance and provides a global point of access to that instance. It is named after the singleton set, which is defined to be a set containing one element.

Problem: This is useful when exactly one object is needed to coordinate actions across the system.

Solution: The implementation involves a static member in the “Singleton” class, a private constructor and a static public method that returns a reference to the static member.

Examples: C++ Code, Logger Classes, Configuration Classes, Accessing resources in shared mode, Factories implemented as Singletons

static StringSingleton &Instance()
{
// This line only runs once, thus creating the only instance in existence
static std::auto_ptr<StringSingleton> instance( new StringSingleton );
// dereferencing the variable here, saves the caller from having to use the arrow operator, and removes temptation to try and delete the returned instance.
return *instance; // always returns the same instance
}

2. Structural Patterns

2.1 Adapter

Definition: Convert the interface of a class into another interface that clients expect.

Problem: Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.

Solution: A Class Adapter — Based on (Multiple) Inheritance

Participants:

  • Target — defines the domain-specific interface that Client uses/expects.
  • Adapter — adapts the interface Adaptee to the Target interface.
  • Adaptee — defines an existing interface that needs adapting.
  • Client — collaborates with objects conforming to the Target interface.

Example: C++ Code, C++ Code

  • Rectangle (desired target)
  • LegacyRectangle (adaptee)
  • RectangleAdapter (adapter): class RectangleAdapter: public Rectangle, private LegacyRectangle. Call oldDraw defined in LegacyRectangle
  • Client (main function): Rectangle *r = new RectangleAdapter(120, 200, 60, 40);

2.2 Bridge

Definition: The Bridge Pattern is used to separate out the interface (decouple abstraction) from its implementation so that the two can vary independently.

Problem: Sometimes an abstraction should have different implementations, inheritance binds an implementation to the abstraction and thus it would be difficult to modify, extend, and reuse abstraction and implementation independently.

Solution:

Participants:

  • Abstraction — Abstraction defines abstraction interface.
  • AbstractionImp — Implements the abstraction interface using a reference to an object of type Implementor.
  • Implementor — Implementor defines the interface for implementation classes. This interface does not need to correspond directly to the abstraction interface and can be very different. AbstractionImp provides an implementation in terms of operations provided by Implementor interface.
  • ConcreteImplementor1, ConcreteImplementor2 — Implements the Implementor interface.

Example: C++ Code

  • Shape (Abstraction): CircleShape, DrawingAPI *m_drawingAPI;
  • DrawingAPI (Implementor): DrawingAPI1, DrawingAPI2

2.3 Composite

Definition: The intent of this pattern is to compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. e.g. Treat the file (leaf) and folder (composite) uniformly as they have many properties in common (size, name, etc)

Problem: There are times when a program needs to manipulate a tree data structure and it is necessary to treat both Branches as well as Leaf Nodes uniformly. Consider for example a program that manipulates a file system where folders contain other folders and files while files are the simple objects or leaves.

Solution:

Participants:

  • Component — Component is the abstraction for leaves and composites. It defines the interface that must be implemented by the objects in the composition. For example, a file system resource defines move, copy, rename, and getSize methods for files and folders.
  • Leaf — Leaves are objects that have no children. They implement services described by the Component interface. For example, a file object implements move, copy, rename, as well as getSize methods which are related to the Component interface.
  • Composite — A Composite stores child components in addition to implementing methods defined by the component interface. Composites implement methods defined in the Component interface by delegating to child components. In addition, composites provide additional methods for adding, removing, as well as getting components.
  • Client — The client manipulates objects in the hierarchy using the component interface.

Example: C++ Code

  • Graphic (Component): Define the basic operation
  • Ellipse (Leaf): Implement the Graphic interface
  • CompositeGraphic (Composite): Implement the Graphic interface with multiple objects
  • Client (main function): use Ellipse and CompositeGraphic uniformly.

2.4 Decorator

Definition: The intent of this pattern is to add additional responsibilities dynamically to an object. Decorators provide a flexible alternative to subclassing for extending functionality. This is also called “Wrapper”. A decorator is different from an adapter in that a decorator changes the object’s responsibilities, while an adapter changes an object interface.

Problem: You want to add behavior or state to individual objects at run-time. Inheritance is not feasible because it is static and applies to an entire class. If your application does some kind of filtering, then Decorator might be a good pattern to consider for the job.

Solution:

Participants:

  • Component — Interface for objects that can have responsibilities added to them dynamically.
  • ConcreteComponent — Defines an object to which additional responsibilities can be added.
  • Decorator — Maintains a reference to a Component object and defines an interface that conforms to Component’s interface.
  • Concrete Decorators — Concrete Decorators extend the functionality of the component by adding state or adding behavior.

Example: C++ Code

  • Widget (Component): TestField (ConcreteComponent), Decorator
  • Decorator: BorderDecorator (Concrete Decorator), ScrollDecorator (Concrete Decorator)
  • Client (main function):
Widget *aWidget = new BorderDecorator(new BorderDecorator(new ScrollDecorator(new TextField(80, 24))));
aWidget->draw();

2.5 Facade

Definition: The Facade Pattern hides the complexities of the system by providing an interface to the client from where the client can access the system on a unified interface. Facade defines a higher-level interface that makes the subsystem easier to use. For instance, making one class method perform a complex process by calling several other classes.

Problem: A segment of the client community needs a simplified interface to the overall functionality of a complex subsystem.

Solution: Use a facade class and the client only needs to access this class to perform the operations of the subsystems.

  • HouseFacade (Facade): Contains members of the type of each subsystem.
  • Alarm (Subsystem)
  • TV (Subsystem)
  • AC (Subsystem)
  • Client (main function): use HouseFacade to call functions of each subsystem

2.6 Flyweight

Definition: The pattern for saving memory (basically) by sharing properties of objects. Imagine a huge number of similar objects which all have most of their properties the same. It is natural to move these properties out of these objects to some external data structure and provide each object with the link to that data structure.

Problem: Designing objects down to the lowest levels of system “granularity” provides optimal flexibility, but can be unacceptably expensive in terms of performance and memory usage.

Solution:

Participants:

  • Flyweight — Declares an interface through which flyweights can receive and act on the extrinsic state.
  • ConcreteFlyweight — Implements the Flyweight interface and stores intrinsic state (sharable among objects). A ConcreteFlyweight object must be sharable. The Concrete flyweight object must maintain state that it is intrinsic to it, and must be able to manipulate state that is extrinsic. In a war game example, a graphical representation is an intrinsic state, where location and health states are extrinsic. Soldier moves, the motion behavior manipulates the external state (location) to create a new location.
  • FlyweightFactory — The factory creates and manages flyweight objects. In addition, the factory ensures the sharing of the flyweight objects. The factory maintains a pool of different flyweight objects and returns an object from the pool if it is already created, adds one to the pool and returns it in case it is new.
  • Client — A client maintains references to flyweights in addition to computing and maintaining extrinsic state.

Example: C++ Code. A key concept is the distinction between “intrinsic” and “extrinsic” state. Intrinsic state consists of information that is independent of the flyweight’s context — information that is sharable (i.e. each Icon’s name, width, and height).

  • Icon (Flywight): store sharable states
  • FlyweightFactory: creates and manages Icon
  • DialogBox: FileSelection, CommitTransaction (Client): Clients should not instantiate Flyweights directly, they should obtain them exclusively from a FlyweightFactory object to ensure they are shared properly:
DialogBox *dialogs[2];
dialogs[0] = new FileSelection(FlyweightFactory::getIcon("go"),
FlyweightFactory::getIcon("stop"),
FlyweightFactory::getIcon("select"));
dialogs[1] = new CommitTransaction(FlyweightFactory::getIcon("select"),
FlyweightFactory::getIcon("stop"),
FlyweightFactory::getIcon("undo"));

2.7 Proxy

Definition: The Proxy Pattern will provide an object a surrogate or placeholder for another object to control access to it. It is used when you need to represent a complex object with a simpler one. If the creation of an object is expensive, it can be postponed until the very need arises and meanwhile a simpler object can serve as a placeholder. This placeholder object is called the “Proxy” for the complex object.

Problem: You need to support resource-hungry objects, and you do not want to instantiate such objects unless and until they are actually requested by the client.

Solution:

Participants:

  • Subject — Interface implemented by the RealSubject and representing its services. The interface must be implemented by the Proxy as well so that the Proxy can be used in any location where the RealSubject can be used.
  • Proxy
    1. Maintains a reference that allows the Proxy to access the RealSubject.
    2. Implements the same interface implemented by the RealSubject so that the Proxy can be substituted for the RealSubject.
    3. Controls access to the RealSubject and may be responsible for its creation and deletion.
    4. Other responsibilities depend on the kind of proxy.
  • RealSubject — the real object that the proxy represents.

Example: C++ Code

3. Behavioral Patterns

3.1 Chain of Responsibility

Definition: Chain of Responsibility pattern has the intent to avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chains the receiving objects and passes the requests along the chain until an object handles it.

Problem: There is a potentially variable number of “handler” or “processing element” or “node” objects, and a stream of requests that must be handled. Need to efficiently process the requests without hard-wiring handler relationships and precedence, or request-to-handler mappings.

Solution:

  1. Put a “next” pointer in the base class
  2. The “chain” method in the base class always delegates to the next object
  3. If the derived classes cannot handle, they delegate to the base class

Participants:

  • Handler — defines an interface for handling requests
  • RequestHandler — handles the requests it is responsible for. If it can handle the request it does so, otherwise, it sends the request to its successor
  • Client — sends commands to the first object in the chain that may handle the command

Example: C++ Code, C++ Code

  • Base: Handler1, Handler2, Handler3
  • Client (main function): can use Base pointers to point to Hanlder1, Hanlder2 and Hanlder3

3.2 Command

Definition: Command pattern decouples sender and receiver by encapsulating a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undo-able operations. It can also be thought as an object-oriented equivalent of call back method.

Problem: Need to issue requests to objects without knowing anything about the operation being requested or the receiver of the request.

Solution:

Participants:

  • Command — declares an interface for executing an operation
  • ConcreteCommand — extends the Command interface, implementing the Execute method by invoking the corresponding operations on Receiver. It defines a link between the Receiver and the action
  • Client — creates a ConcreteCommand object and sets its receiver
  • Invoker — asks the command to carry out the request
  • Receiver — knows how to perform the operations

Example: C++ Code

  • Command: FlipUpCommand, FlipDownCommand (ConcreteCommand)
  • Light (Receiver):
  • Switch (Invoker): Call FlipUpCommand.execute() or FlipDownCommand.execute()
  • Client (main function): Use Switch to control the light.

3.3 Interpreter

Definition: Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.

Problem: A class of problems occurs repeatedly in a well-defined and well-understood domain. If the domain were characterized with a “language”, then problems could be easily solved with an interpretation “engine”.

Solution: The implementation of the Interpreter pattern is just the use of the composite pattern applied to represent a grammar. The Interpreter defines the behavior while the composite defines only the structure.

Participants:

  • Context — keeps the current string that has to be parsed and the decimal that contains the conversion already done. Initially, the context keeps the full string that has to be converted and 0 for the output decimal.
  • Expression — Consists of the interpret method which receives the context. Based on the current object it uses specific values for Thousand, Hundred, Ten, One and a specific multiplier.
  • ThousandExpression, HundredExpression, TenExpression, OneExpression (TerminalExpression) — Those classes are used to define each specific expression. Usually, the TerminalExpression classes implement the interpret method. In our case, the method is already defined in the base Expression class and each TerminalExpression class defines its behavior by implementing the abstract methods: one, four(), five(), nine(), multiplier(). It is a template method pattern.
  • Main(Client) — In our little example, this class is responsible to build the syntax tree representing a specific sentence in the language defined by the grammar. After the syntax tree is built the main method is invoking the interpret method.

Example: C++ Code, Java Code

3.4 Iterator

Definition: The basic idea of the iterator is that it permits the traversal of a container (like a pointer moving across an array). However, to get to the next element of a container, you need not know anything about how the container is constructed. This is the iterators job. By simply using the member functions provided by the iterator, you can move, in the intended order of the container, from the first element to the last element.

Problem: Need to “abstract” the traversal of wildly different data structures so that algorithms can be defined that is capable of interfacing with each transparently.

Solution:

  1. Design an “iterator” class for the “container/aggregate” class
  2. Add a createIterator() member to the container/aggregate class
  3. Clients ask the container object to create an iterator object
  4. Clients use the first(), isDone(), next(), and currentItem() protocol

Participants (collection of books):

  • IIterator — This interface represents the AbstractIterator, defining the iterator
  • BookIterator — This is the implementation of Iterator(implements the IIterator interface)
  • IContainer — This is an interface defining the Aggregate
  • BooksCollection — An implementation of the collection

Example: C++ Code

  • Stack (ConcreteAggregate)
  • StackIter (ConcreteIterator)
  • Client (main function)

3.5 Mediator

Definition: Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently. In order to avoid tightly coupled frameworks, we need a mechanism to facilitate the interaction between objects in a manner in that objects are not aware of the existence of other objects.

Problem: We want to design reusable components, but dependencies between the potentially reusable pieces demonstrate the “spaghetti code” phenomenon (trying to scoop a single serving result in an “all or nothing clump”).

Solution:

Participants

  • Mediator — defines an interface for communicating with Colleague objects.
  • ConcreteMediator — knows the colleague classes and keep a reference to the colleague objects. It implements communication and transfers the messages between the colleague classes.
  • Colleague classes — keep a reference to its Mediator object, it communicates with the Mediator whenever it would have otherwise communicated with another Colleague.

Example: C++ Code

3.6 Memento

Definition: Without violating encapsulation the Memento Pattern will capture and externalize an object’s internal state so that the object can be restored to this state later.

Problem: Need to restore an object back to its previous state (e.g. “undo” or “rollback” operations).

Solution:

Participants:

  • Memento
    1. Stores internal state of the Originator object. The state can include any number of state variables.
    2. The Memento must have two interfaces, an interface to the caretaker. This interface must not allow any operations or any access to internal state stored by the memento and thus honors encapsulation. The other interface is to the originator and allows the originator to access any state variables necessary for the originator to restore the previous state.
  • Originator
    1. Creates a memento object capturing the originator's internal state.
    2. Use the memento object to restore its previous state.
  • Caretaker
    1. Responsible for keeping the memento.
    2. The memento is opaque to the caretaker, and the caretaker must not operate on it.

Example: C++ Code

  • Number (Originator): Memento *createMemento()
  • Memento (Memento): friend class Number;
  • Command (Caretaker): Number* receiver; static vector<Command*> commandList; static vector<Memento*> mementoList;
  • Client (main function): Use number and command

3.7 Observer

Definition: The Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Problem: In one place or many places in the application we need to be aware of a system event or an application state change. We’d like to have a standard way of subscribing to listening for system events and a standard way of notifying the interested parties. The notification should be automated after an interested party subscribed to the system event or application state change. There also should be a way to unsubscribe.

Solution: After subscribing the listening objects will be notified by a way of the method call.

Participants:

  • Observable — interface or abstract class defining the operations for attaching and de-attaching observers to the client. In the GOF book, this class/interface is known as Subject.
  • ConcreteObservable — concrete Observable class. It maintains the state of the object and when a change in the state occurs it notifies the attached Observers.
  • Observer — interface or abstract class defining the operations to be used to notify this object.
  • ConcreteObserverA, ConcreteObserverB — concrete Observer implementations.

Example: C++ Code

  • Subject (Observable): has many observers, vector < class Observer * > views; When notify is called, update of observer is called as well.
  • Observer: DivObserver, ModObserver: use subject as a parameter of the constructor to call subject->attach(this).

3.8 State

Definition: The State Pattern allows an object to alter its behavior when its internal state changes. The object will appear as having changed its class.

Problem: An object should change its behavior when its internal state changes. State-specific behavior should be defined independently. That is, adding new states should not affect the behavior of existing states.

Solution:

Participants:

  • Context: The Context object delegates state-specific behavior to different State objects.
  • State

Example: C++ Code

  • Machine (Context): Has a member of State. State *current;
  • State: On, Off: Receive machine pointer. virtual void on(Machine *m); virtual void off(Machine *m);

3.9 Strategy

Definition: Defines a family of algorithms, encapsulates each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients who use it.

Problem:

Solution:

Participants:

  • Strategy — defines an interface common to all supported algorithms. Context uses this interface to call the algorithm defined by a ConcreteStrategy.
  • ConcreteStrategy — each concrete strategy implements an algorithm.
  • Context
    1. contains a reference to a Strategy object. StrategyInterface * strategy_;
    2. may define an interface that lets strategy accessing its data.

Example: C++ Code, Example

  • Context: Can be TransportationToAirport, can setStrategy and execute strategy by calling strategy_->execute();
  • Strategy: CityBus, PersonalCar, Taxi, define an execute function.

3.8 Template Method

Definition: By defining a skeleton of an algorithm in an operation, deferring some steps to subclasses, the Template Method lets subclasses redefine certain steps of that algorithm without changing the algorithm’s structure.

Problem: Two different components have significant similarities, but demonstrate no reuse of common interface or implementation. If a change common to both components becomes necessary, duplicate effort must be expended.

Solution:

Participants:

  • AbstractClass — defines abstract primitive operations that concrete subclasses define to implement steps of an algorithm.
  • ConcreteClass — implements the primitive operations to carry out subclass-specific steps of the algorithm. When a concrete class is called the template method code will be executed from the base class while for each method used inside the template method will be called the implementation from the derived class.

Example: C++ Code

  • Game: Monopoly, Chess
Game* game = nullptr; //pointer to base calss
Chess chess;
game = &chess; //points to object of subclass Chess
for (unsigned i = 0; i < 100; ++i)
game->playOneGame();
Monopoly monopoly;
game = &monopoly; //points to object of subclass Monopoly
for (unsigned i = 0; i < 100; ++i)
game->playOneGame();

3.8 Visitor

Definition: The Visitor Pattern will represent an operation to be performed on the elements of an object structure by letting you define a new operation without changing the classes of the elements on which it operates.

Problem: Collections are data types widely used in object-oriented programming. Often collections contain objects of different types and in those cases, some operations have to be performed on all the collection elements without knowing the type.

Solution:

Participants:

  • Visitor — This is an interface or an abstract class used to declare the visit operations for all the types of visitable classes. Usually, the name of the operation is the same and the operations are differentiated by the method signature: The input object type decides which of the method is called.
  • ConcreteVisitor — For each type of visitor all the visit methods, declared in abstract visitor, must be implemented. Each Visitor will be responsible for different operations. When a new visitor is defined it has to be passed to the object structure.
  • Visitable — is an abstraction which declares the accept operation. This is the entry point which enables an object to be “visited” by the visitor object. Each object from a collection should implement this abstraction in order to be able to be visited.
  • ConcreteVisitable — Those classes implement the Visitable interface or class and define the accept operation. The visitor object is passed to this object using the accept operation.
  • ObjectStructure — This is a class containing all the objects that can be visited. It offers a mechanism to iterate through all the elements. This structure is not necessarily a collection. In can be a complex structure, such as a composite object.

Example: C++ Code

  • Car: vector<CarElement*> elements_;
  • CarElement: Wheel, Engine, Body
  • CarElementVisitor: CarElementPrintVisitor, CarElementDoVisitor, pass car to function: void visitCar(Car& car) const

Example: C++ Code

--

--