Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
327 lines (192 sloc) 19.6 KB

Design Patterns quick overview

This article is a cheat sheet of the most used design patterns. This was not made to teach you. This was made to make you remember what you already understood.
Other sources to learn and understand more:

Note: It is very important not to try to use a given pattern as it is. Perhaps you just want to use the wrong pattern. Always adapt them to the requirements to solve a given task. It is very frequent that more patterns are used together to achieve the goal.

There are patterns which are similar to other patterns. The intention why to choose one, how to use one, what to solve with one makes the difference.

Basic Definitions

Several design patterns based on the following key concepts:

Interface: a structure of a type

(usually a class) without a specific implementation. It is a contract between two objects. Interfaces usually are declared by objects to 'delegate' some functionality (see Inversion of control for more detail). Specifies what methods an object can have - but does not specify the behaviour. Different classes implements the interface providing different behaviours.

Wrapper: holds reference to an other object. Sometimes it means the owner manages the lifetime of that object as well, sometimes just a simple reference to use it. To make it flexible this reference should be kept using an `interface` instead.
Delegation: move/use functionality of another class.

The purpose can be to ask for some data or to perform an action (transport the data). Apply it using the wrapper.

Factory: a method/class which creates objects

Factory is a method (or a class with more methods) which has the only task to manage the creation of other classes. Factory usually provides objects through interface. See Factory method and/or Abstract factory for more details.


GoF Design patterns

GoF is the abbreviation of Gang of Four - after the four authors of one of the most fundamental design patterns book. There are three different kinds of pattern based on their tasks:

  • Creational patterns: how objects can be created/accessed
  • Structural patterns: how objects can be bind together
  • Behavioral patterns: how objects works, works together to achieve specific tasks

Creational patterns

Abstract Factory: creates and returns with related classes

"Provide an interface for creating families of related or dependent objects without specifying their concrete classes."

Client uses a factory (referencing Abstract Factory, through interface or abstract class) to create instances of different classes belonging together.

Define an interface/class which declares the methods used by the client and add different implementation of it by overriding the creational methods. This way the client can build products with the same structure using the factory by calling the appropriate method (Delegation)

Abstract Factory is often implemented using Template Methods or Prototype.

For example: HouseFactory: createWall, createDoor, createWindow ... Different implementations of HouseFactory create different parts of a house, but one instance creates the same "style" of house.

Builder: creates a _product_ in several steps; _director_ directs _builder_, what part to add to the product and when. Finally _director_ asks _buidler_ to create the final _product_.

"Separate the construction of a complex object from its representation so that the same construction process can create different representations." Director class sets up a complex object by calling elementary methods defined in the Builder interface/class. Each builder subclass implements the operations in its own way so Director won't know what parts makes up the object and how are they assembled. Finally Director asks builder to create a 'product'.

Similar to Abstract factory, but the purpose is different. While the client in abstract factory gets a new object for each call, the Builder doesn't give out the object. The Director (client) directs the Builder what to do.

Can be used with abstract factory - inside the builder. For example: HouseBuilder: addWall, addWindow, addDoor, create/get House

Factory method: create an instance of some class conforming to a given interface or base class; subclasses can return with different instances

"Define an interface for creating an object, but let subclasses decide which class to instantiate"

Put a dedicated method to a class (Creator) which creates the Product. Creator also can contain other methods which is used with Product. The subclass of Creator will implement the method and create the specific Product instance.

Prototype: create new instances by copying an existing one

Create new instances of a class using an existing one by cloning/copying it. Very useful, when the creation of new instances needs in runtime while the classes are referenced by an interface/base-class.

Singleton: allow only one instance from a class

"Ensure a class only has one instance, and provide a global point of access to it"

Instance of a class can be accessed through only one dedicated 'static/class' method. Keep the only instance inside the class scope privately. The allocation/creation of the class instance have to be prohibited to other classes.

> Note: Be careful and do not overuse this pattern. Very easily can became an anti-pattern. I would say in 99% of the cases you don't need it. For more information why, see [here](http://andras.palfi.hu/singleton-the-anti-pattern).  

Structural Patterns

Adapter: make classes compatible with another class, interface

Implement an interface for a class to make it compatible with another class which wants to use the first class.

Two kinds:

  • Class adapter: use multiple inheritance/interface implementation to implement the new interface while subclassing the original class.
  • Object adapter: create a new class implementing the desired interface and encapsulate (wrapper) the original class holding reference to it in a member. In the interface-methods call the methods of the encapsulated class (see: proxy).
Bridge: implement complex things for multiple platform delegating base operations

"Decouple an abstraction from its implementation so that the two can vary independently"

Useful when the same functionality for problems has to be implemented to more platforms. For the two platforms two implementation needed but the real differences are only at low level.
For example build houses, where the structure always the same, but perhaps you use different bricks. So you have a house builder which has an internal wall builder which puts together the bricks. If everything hard coded you implement two house builders but the majority of the code is duplications. To get rid of code duplications the easiest way is to replace only the bricks which are used to build the house.
The same is drawing shapes on different systems. It is enough to define some basic operations (draw line, draw curve) and then implement the draw shape methods (rect, triangle, circle, more complex stuff) using these basic operations.

In short: delegate the basic operations, then implement what you need using abstract references to that basic operations.

Composite: build a hierarchy

Compose objects to hierarchies by using a base class/interface which contains the very basic mandatory operations to manage the hierarchy and perform basic operations.

Declare node classes and leaf classes. Calling a basic operation on a node will forward the same call to all its children. Hierarchy management operations will work only on node items.

Decorator: attach additional functionality/feature to an existing component on the same API by wrapping it into a new compatible class

It helps to add functionality dymaically and you can vary the features independently!

For compatibility reason the classes need a base class/interface.
Decorator keeps a reference to the All operations called on the decorator will be forwarded to the wrapped class but performing additional operations before and/or after the forwarded call.

Facade: publish a simpler API above a system to hide underneath API

To hide, simplify or control a complex class hierarchy create and publish a new interface/class. Delegate all operation to the appropriate subsystem classes. Users of the facade don’t know about the classes in the subsystem and subsystem classes don’t know about the facade or the user classes.
It is also easier to change the underlying classes.

Flyweight: share expensive objects among class instances

Share the expensive objects (flyweights) among instances if there are numerous elements in a system which use the same components.
Do not store object specific states in the flyweights but pass the actual state to them on use.

  • Client can store the states and pass them to flyweights when needed.
  • An other aspect that the client only references light flwyweight objects which stores the state and calls the real flyweights (expensive objects stored by flyweight pool) and the real flywights are invisible for client.

Can be very useful for example to image caches. It is possible to remove unnecessary images from memory and load them on demand even if the 'image flyweight' object is referenced by the system since that only references a container (flyweight pool) which stores the real image.

Proxy: a compatible wrapper to control the access to the original object

Wrap the original object into the proxy and forward every call to it. Proxy has the same API as the original object, so it is 'invisible'.

Subcases:

  • remote proxy: provides a local representation of a remote object in a different address space like network, database etc.
  • virtual proxy: create the wrapped object on demand – lazy binding.
  • protection proxy: filter the access to an object
  • smart proxy: the proxy performs some additional functionality; useful for reference counting and lifetime management (smart pointer); thread locking etc

Behavioral Patterns

Chain of Responsibility: Chain the receiving objects and pass the request along the chain until one handles it

Base on the original pattern description the objects are composed to a chain. When an item is called it handles the message or it calls the base class implementation, which by default calls the next item in the chain. For that all items in the chain must be a subclass of a specific base class.

Can be implemented however otherwise where the object in teh chain just returns whether the message was handled or not and the caller (parent object) knows who to call next. Can be used with iterator.

Command: encapsulate _requests_ as objects instead of method call on an object with specific parameters

Encapsulate a request as an object: the command should encapsulate the receiver and the parameters. Client just calls the execute method. It is possible to queue or log requests, command can encapsulate its reverse operation to support undo.

Another variation when only the parameters are stored in a command object and using chain of responsibility we let someone to handle it. Operating systems' user interfaces work this way.

Interpreter: define a language with its interpreter (processor)

Define a language representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.
The language can be letters, numbers or even color codes.

Interpreter always pass the state to the next interpreter. Usually implemented with Composite.

Iterator: provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation

Given aggregate objects (array, dictionary, lists, trees etc) and walk though each item one by one without knowing the internal structure. It is also possible to walk through items from one to the last or reversed order or using any specific rule (items conforming to a rule).

Define an interface with next() method.
Let's create a new iterator classes for each storage (and rule) which knows the internal structure of the collection and has access to its items.

Clients can instantiate the proper iterator and call next() accessign all the elements (conforming to a rule, accessing them in the proper order) until the end of the items (next() returns null, or dedicated finished() returns true)

Mediator: define a supervisor object which knows who to call and when

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 interactions independently. The underlying objects don’t hold reference to each other but a reference to the mediator object (through an interface or abstract class). The mediator has reference to the more important underlying objects.
Even if underlying objects call each other or a client makes a call mediator direct the call to the proper underlying object. Similar to Facade but the intention is different as it simplifies the API and facade handles only requests from clients.

Memento: serialization, capture an object state, store and restore te object later

Caretaker an object knowing when to use (save/restore) originator. It can be a client directly. Calls a dedicated method on originator to create a memento (persisted state object) and another method to set the back the stateof the object to the data captured in memento

Observer: pusblish-subscribe; more objects listen on changes of a subject object

Define a one-to-many dependency between objects. The observers listen to the changes of subject (observed object) so that when the subject changes its state, all its observers are notified. Usually the subject sends out specific messages which the observers catch/handle.

State: instead of conditions everywhere in the code use separated objects with the same API but different behavior according to state.

Use, when the state of an object changes at runtime and it is hard to use conditions everywhere in the code.
The client holds reference to the context class which is technically a proxy providing the same API as all the state objects used internally in context. The context class holds reference to a specific state object and forward every call to it. If the state should change the context class will reference another state class.

Strategy: replace functionality referenced by an interface/base class

Use when it is necessary to be able to easily replace some functionality. Client can be configured with any concrete 'strategy' implementations as it references a strategy with an interface. Delegation: client delegates the functionality to a strategy

Template method: define abstract/virtual methods what subclasses must implement

Different subclasses can implement the template method differently.

Prefer strategy and delegation instead.

Visitor: perform tasks for instances of different types of classes in a composite structure without type casting using method overload

Given a composite strcture containing different, related types of classes. Without extending the classes let to perform different tasks.

  • The original classes declares a special method - accept(visitor) where visitor is an interface/base class with methods: visit(type) where the visit method overloaded with all the types in the composite.
  • accept only calls the appropriate visit() method selected by the compiler based on type
  • create different visitors performing different tasks with the items
  • apply/perform a given visitor instance passing it to composite. It calls all the items' accept method which just calls the appropriate visit method on it.

Other design patterns

Object Pool: limit the number of objects

There are situations when the number of objects must be limited. Use a dedicated object which controls the instances available of (another) object. Can be a separate object store but can be implemented on the class itself as static method.
YourClass.getFreeObject() can return null or can block the call waiting for the next available instance.
The user of an object must release the instance when finished.

Object pool can also behave in a way that always creates a new instance but it is also possible to push an unused object into it so next time it returns with the existing object instead of creates a new one.

Independent object store can store different types of objects. Passing the type (or using generics) the proper type of instance can be retrieved.

As en example think of thread pools or network request pools

Multitone: more instance of a type referenced by key

Similar to singleton, multitone however supports more instances. The access of the objects are bound to a key.
Different instances of course have different states. Based on implementation it can have different behavior etc.

A case of multitone which provides the objects where the input is a type (type can be input parameter or methods can be generic).

Private class data / Opaque pointer / pImpl: hide data/code in a membber

pImpl (pointer to implementation): the details of the type of member is hidden/unkown - only private implementation knows it.

This design patterns used mostly in languages where the structure of private members are visible (c++, Objective-C etc)
In this languages a "forward declaration", an empy name declaration is enough to represent the storage of a data or functional object in a member. It can be a non-typed representation as well (void*);

The implementation internally can use the pImpl instance or even just forward the calls to it.

Null object: provide a simplistic, empty, default implementation for an interface/abstract class

When an object must reference something but it can be "empty" instead of using Null/Nil as referene and alwasy check the existance, provide a very primitive, empty implementation as a placeholder.

Repository pattern
You can’t perform that action at this time.