Design Patterns

Your Quick Reference to Design Patterns

A curated list of most commonly used OOP design patterns

Sabbir Mollah
11 min readJan 4, 2020

--

Someone has already solved your problem

The problems you face during software development might have been already solved decades ago. It is only inefficient trying to reinvent a solution to these existing and common problems.

Quick Reminder: Don’t reinvent the wheel

These solutions have been recorded with use case scenarios and implementations and are known as design patterns.

So, what is a design pattern?

A software design pattern is a general, reusable solution to a commonly occurring problem within a given context.

How to read this article

If you came here to learn about design patterns, you definitely are in the wrong place. This article should be used more like a quick reference to the mostly used design patterns during your development phase.

If you need more detailed reference materials, you can read [1]Design Patterns: Elements of Reusable Object-Oriented Software.

If you need to learn design patterns from scratch head over to [2]Head First Design Patterns book.

The examples in this article will be written in Java.

Every pattern will have 4 sections each.

  • Definition — Formal definition of the pattern (All the definitions are taken from the book [2])
  • When? — When to use the pattern
  • How? — Implementation in code or UML diagrams
  • Wait-A-Sec — Notes about the pattern

Also, conventional UML notations will be used in the UML class diagrams.

1. Singleton Pattern

The Singleton Pattern ensures a class has only one instance, and provides a global point of access to it.

  • When?

Sometimes you may wish your class to have only one object or instantiation. For example, you might want only one FileManager or DatabaseManager instance running on your system. In those cases you make the class as a singleton.

  • How?

You will not see any class diagram for this pattern, because DUH… It’s a creational pattern oriented on a single class.

So, now to get an instance of the class or to create an instance for the first time:

Singleton singleton = Singleton.getInstance();
  • Wait-A-Sec

There are debates on whether one should use this pattern. The main point is that Singleton classes are difficult to test and also it breaks the program if in future the client requires the class to have multiple instances.

The implementation shown here is not thread safe. To learn about a better version of this pattern checkout this article.

2. Strategy Pattern

The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

  • When?

Strategy pattern is used to provide more flexibility in the behavior of an object. In this pattern the behaviors of an object can be set at runtime.

In a beginner level of object oriented course we are introduced to inheritance as a very powerful tool. Let’s say we are told to design a class diagram for the different kind of animals in a zoo. What seems to be an obvious solution is to use inheritance. Let’s look at the problems of inheritance first.

The four sub types of animals in this example all inherit the shout() method from the Animal class. Now, if the Panda and Bear class have the same kind of shout(), then you would have to write the algorithm in one class and COPY-PASTE it in another. But if one thing we know about development is that we need to keep our code DRY.

Design Principle: Don’t Repeat Yourself.

Repeating is bad because each time you have to modify your algorithm, you’ll have to modify it in every other places you’ve copy pasted that algorithm.

Another problem in the above inheritance example is the shout() of Snail. Notice how Snail is inheriting the shout() method even though a snail shouldn’t have any kind of shouting mechanism.

  • How?

We can design a better solution by making use of interfaces.

Notice how this time the methods can be shared through sub classes. The behaviors are now set on runtime through the Client class’s constructor method.

Design Principle: Design to an interface, not an implementation

  • Wait-A-Sec

Whenever you find yourself using if/else in your code, ask yourself if you can use Strategy Pattern instead.

3. Observer Pattern

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

  • When?

This pattern is used when one or multiple objects want to know about a certain change in the state of an object.

Imagine your mother is preparing food. You and your other 4 siblings are constantly asking her every second whether food is ready yet. Now, this is very inefficient and chances are that you will end up getting the food late because your mother is now mad at you all. A more elegant approach would be for you to wait for your mother until she notifies you that food is ready.

This pattern is used in most notification and event based programs.

  • How?
Observer Pattern UML Diagram

Whenever the state changes in the Observable, it calls notify which is implemented as:

public void notify(){
for(IObserver observer: observers){
observer.update();
}
}

Notice the fact that ConcreteObserver has a composition relation with ConcreteObservable. This is necessary for the observers to access the getState() or any equivalent method in the Observervable.

4. Adapter Pattern

The Adapter Pattern converts the interface of a class into another interface the client expects. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.

Adapter Dongle Example
  • When?

Adapter design pattern is used to work with two incompatible interfaces. Just like how you would use an “Adapter” to connect an audio jack to a iPhone without the audio port.

Using an adapter gives you some additional features and flexibility on how you want to use a certain class (adaptee). You can also think of the Adapter as a Wrapper.

Point Class example for Adapter Pattern

Let’s say, you have a library class called PolarPoint which you cannot modify. PolarPoint works with polar coordinates which you are not familiar with. To use PolarPoint but in a Cartesian coordinate system you have to make use of an adapter.

  • How?

The Client can perform the action() without knowing the underlying conversions.

ITarget target = new Adapter( new Adaptee() );
target.action();

In the example above, the display method in the PointAdapter class would be defined as:

//Point class
public void display(double x, double y){
double length = sqrt(x1^2 , x2^2);
double angle = arctan(y/x);
polarPoint.display( length, angle);
}

5. Factory Method Pattern

The Factory Method Pattern defines an interface for creating an object, but lets sub-classes decide which class to instantiate. Factory Method lets a class defer instantiation to sub-classes.

  • When?

Factory Pattern is used when we want to “choose” and initialize one of several classes that are sub classes to a common super class.

For example, let’s say your restaurant software has a super abstract class “Food”. And different kind of food classes such as “Pizza”, “Burger” etc. Now, if you want to initiate a concrete food object depending on the customers choice, you would need to write some if else conditions.

Food food;
if( customerChoice == "Pizza" ){
food = new Pizza();
}
else if( customerChoice == "Burger" ) {
food = new Burger();
}
...

The problem is, you may have this portion of code in several places in your code, and one thing we have learnt till now is that repetition is bad. Whenever you’ll have to modify something (let’s say you are adding a new kind of Food in your restaurant) you’ll have to go through all recurrences and modify this snippet.

So the solution to it would be having a separate class that handles the instantiation of our classes. This way we can include all the business logic of the instantiation into the new Factory class.

  • How?

Let’s directly look at the general diagram:

Factory Pattern general diagram

Now, we can include all the business logic for creating any ConcreteProduct inside the createProduct() function in ConcreteFactory class.

Product createProduct(){
Food food;
if( customerChoice == "Pizza" ){
food = new Pizza();
}
else if( customerChoice == "Burger" ) {
food = new Burger();
}
return food;
}
  • Wait-A-Sec

The ConcreteFactory has a compositation relation with all the ConcreteProduct sub classes. This is against the Dependency Inversion Principle.

Design Principle: Depend upon abstractions. Do not depend upon concrete classes.

There are ways to further improve the implementations of this pattern. Please refer to a more extensive resource.

6. Decorator Pattern

The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub-classing for extending functionality.

  • When?

Assume you own a restaurant that serves 3 base food items. Each of these item can be equipped with any number and combination of 4 toppings or extensions.

Your first thought may be to have one class for every possible combination of food item with toppings. This way you can easily calculate the cost. However, this will just create a class explosion.

To avoid the class explosion you may think of the solution in a different way. That is to have booleans in your Food class to indicate the selection of each topping.

2nd possible solution

Though this may seem a good solution at the first glance, it breaks the open close design principle.

Design Principle: Software Entities (Classes for example) should be open for extension, but closed for modification.

That is, every time you want to add a new kind of topping, you’ll have to modify your base Food class. And this is unwanted. Your program should be flexible for extensions without modifying the classes. Beside that, this second solution also isn’t suitable when all the FoodItems are not suitable with all kind of toppings. You wouldn’t want Pizza to come with Chocolate topping.

You can come up with a clean design for this solution by using the Decorator Pattern.

  • How?
Decorator Pattern General UML

Solution to the above mentioned problem:

Decorator Pattern solution to the mentioned problem

Notice how the Decorator class is-a Component and also has-a Component.

So we can easily wrap toppings to our food in the following pattern:

Food food = new Burger();
food = new Cheese(food);
food = new Fries(food);

This way our food is now wrapped inside the Cheese topping which then again is wrapped in Fries topping.

The getCost() function would be implemented as:

// Inside a ConcreteDecorator
// This will be continuously called recursively
public double getCost(){
return this.cost + this.food.getCost();
}
// The getCost() of a ConcreteComponent
// This will stop the recursive call
public double getCost(){
return this.cost;
}

7. Template Method Pattern

The Template Method Pattern defines the skeleton of an algorithm in a method, deferring some steps to sub-classes. Template method lets sub-classes redefine certain steps of an algorithm without changing the algorithm’s structure.

  • When?

Template method is a very commonly used design pattern. This pattern is used to reuse a function that might change partially depending on the implementations. For example the steps to prepare Tea or Coffee is very similar.

Similarities of preparing tea or coffee

As you can see all the tasks to make your drink are the same except two. Also notice that the order of these tasks is important, and you don’t want someone to alter the order.

  • How?

This pattern is implemented by making use of abstract class and inheritance.

Design for tea or coffee problem

To make sure that the prepareDrink() method in HotDrink class is not further overriden we can declare the function as final in Java.

Here is a general diagram of how to implement this pattern.

Template Pattern general UML
  • Wait-A-Sec

It is always advisable to not see inheritance as a mean to reuse code. As we have seen in the Strategy Pattern section, inheritance doesn’t give us any way to share code among children of a class. So, use this pattern when you are absolutely sure that this is what you actually need.

8. Composite Pattern

The Composite Pattern composes objects into tree structures to represent part-whole hierarchies. Composites let’s client treat individual object and compositions of objects uniformly.

  • When?

This pattern is used whenever we need a way to treat a group of objects as if it were one.

Think of a drawing software. There are some basic shapes such as Circle, Rectangle, Line etc. You can move, change fill color or do similar actions on these objects. Now, by using a group of shapes you could have a more complex drawing. The idea is that you should be able to perform these same actions on this group as if it were a single shape.

  • How?
Composite Pattern General UML

Keep an eye on the composite pattern as it is-a Component and has-many Components. Since it is a Component it can have the same operation() or more operations of a single Component. And since it has many Components it can be a group of many Components. The group may be implemented as a list, a tree or whatever data structure suits the use case.

  • Wait-A-Sec

Since the Component class has an add() function, it may cause a problem since it has no meaning in Leaf class. One way to tackle this issue would be to use try and catch to ensure that no Leaf can add() other Components.

Wrapping up

Of course these are not the only patterns available. This article will be continuously updated with edits and new patterns will be added.

References

  1. Gamma, Helm, Johnson, Vlissides. Design Patterns. Pearson, 1994. Available from here.
  2. Eric Freeman, Elisabeth Freeman, Sierra, Bates. Head First Design Patterns. O’Reilly, 2009. Available from here.

--

--