A Quick Guide to Design Patterns
Design patterns explained with simple examples
1- Strategy Pattern
“Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use them.”
Aside from formal definition, Strategy Pattern is widely used when you need to switch your algorithm or strategy in different places of your code.
Simple Example:
Suppose you build a game that has a Cat class that implements a Diet class. But let’s say your cat gained too much weight and now it needs to go on a diet that will help it lose weight for its health.
In this case, you can implement a Strategy Pattern such as:
interface EatBehavior {
public void eat();
}
diet types that implements the eat behavior
public class NormalDiet implements EatBehavior {
@Override
public void eat() {
// normal food
}
}public class LosingWeightDiet implements EatBehavior {
@Override
public void eat() {
// healthy food
}
}
cat class that HAS-A eat behavior
public abstract class Cat { EatBehavior eatBehavior; public Cat(){}
public void eat() {
eatBehavior.eat();
}
}
And the client code would be like this:
cat.eat(); // Cat eats as usual.cat.setEatBehavior(new LosingWeightDiet());cat.eat(); // Cat eats with a healthier diet.
Strategy Patterns can also help you assign different behaviors for different subtypes.
Let’s say you have a spyhnx cat (a species of cat that doesn’t have any fur) and a tabby cat (or any other cat species that has a fur).
You can easily update your classes as follows:
interface EatBehavior {
public void eat();
}
interface DisplayBehavior {
public void display();
}
fur types that implements the display behavior
public class HasFur implements DisplayBehavior {
@Override
public void display() {
// display with fur
}
}public class NoFur implements DisplayBehavior {
@Override
public void display() {
// display without fur
}
}
a cat class that IS-A Cat type and HAS-A eat and display behavior
public class Sphynx extends Cat { EatBehavior eatBehavior;
DisplayBehavior displayBehavior; public Sphynx() {
eatBehavior = normalDiet();
displayBehavior = noFur();
} public void eat() {
eatBehavior.eat();
} public void display() {
displayBehavior.display();
}
}
another cat class that IS-A Cat type and HAS-A eat behavior with a different display behavior
public class Tabby extends Cat {
EatBehavior eatBehavior;
DisplayBehavior displayBehavior; public Tabby() {
eatBehavior = normalDiet();
displayBehavior = hasFur();
} public void eat() {
eatBehavior.eat();
} public void display() {
displayBehavior.display();
}
}
And the client code would be like this:
Cat spyhnx = new Spyhnx();
sphynx.display(); // displays cat without furCat tabby = new Tabby();
tabby.display(); // displays cat with fur
As seen above, it can also help you separate concerns of classes.
If one day you want to remove fur from the tabby for your game, you can just change how displayBehavior is set in the Tabby class and the client won’t need to be notified of this change.
Each class will only care of its own. (Single Responsibility Principle)
Real-Life Example:
Suppose you are writing a software that will show weather forecasts but you want to store the user information in a database and weather information in another database.
different database types that implements the database strategy
public class PostgreStrategy implements DatabaseStrategy {
@Override
public void save() {
// save to PostgreSQL
}
}public class CouchbaseStrategy implements DatabaseStrategy {
@Override
public void save() {
// save to Couchbase
}
}
The client code:
DatabaseContext db = new DatabaseContext();db.setDatabaseStrategy(new PostgreStrategy());
db.save(userInformation);db.setDatabaseStrategy(new CouchbaseStrategy());
db.save(weatherInformation);
Again, if one day you want to change the way how PostgreStrategy or CouchbaseStrategy works, you won’t need to touch the client code.
In short, Strategy Pattern helps us to dynamically change the way our components work as we write code and separates our concerns.
To read it as a single story:
Strategy Pattern — A Quick Guide (3 min read)
2- Decorator Pattern
“Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.”
Aside from formal definition, Decorator Pattern wraps around an object of a Decorator class, allowing you to dynamically change the behavior of an object at client code.
Simple Example:
Suppose you own a local pizza shop that has “have-it-your-way” style pizzas.
Its software has Pizza superclass with subclasses such as PizzaWithMushrooms, PizzaWithCorn, PizzaWithSalami, PizzaWithMushroomsAndCorn, PizzaWithMushroomsAndSalami, PizzaWithCornAndSalami, PizzaWithMushroomsAndCornAndSalami.
That’s not a very good practice, is it?
What if we want to add a new ingredient in the future? What if we want to add a new crust type, like Thin Crust Pizza? What if we want to update prices of each ingredient? What if we want to add x2 more corn?
In our case, this practice would result in a maintenance nightmare.
Our goal must be to allow our classes to add new behaviors, without modifying existing code. (Open-Closed Principle)
Written code should be reusable.
To achieve this, we can implement Decorator Pattern such as:
public interface Pizza {
String getSummary();
int getPrice();
}
pizza classes that implements the Pizza interface
public class StandardCrustPizza implements Pizza private static final String PRICE = 25;
@Override
public String getSummary() {
return "Standard Pizza (" + PRICE + ")";
} @Override
public int getPrice() {
return PRICE;
}
}public class ThinCrustPizza implements Pizza { private static final String PRICE = 30;
@Override
public String getSummary() {
return "Thin Crust Pizza (" + PRICE + ")";
} @Override
public int getPrice() {
return PRICE;
}
}
pizza decorator abstract class that implements the Pizza interface
abstract class PizzaDecorator implements Pizza { @Override
public abstract String getSummary();
@Override
public abstract int getPrice();
}
pizza ingredients that extends PizzaDecorator abstract class
public class Corn extends PizzaDecorator {
private Pizza pizza;
private static final String PRICE = 4;
Corn(Pizza pizza) {
this.pizza = pizza;
} @Override
public String getSummary() {
return pizza.getSummary() +
", with corn (" + PRICE + ")";
} @Override
public int getPrice() {
return pizza.getPrice() + PRICE;
}
}public class Mushrooms extends PizzaDecorator { private Pizza pizza;
private static final String PRICE = 6; Mushrooms(Pizza pizza) {
this.pizza = pizza;
} @Override
public String getSummary() {
return pizza.getSummary() +
", with mushrooms (" + PRICE + ")";
} @Override
public int getPrice() {
return pizza.getPrice() + PRICE;
}
}public class Salami extends PizzaDecorator {
private Pizza pizza;
private static final String PRICE = 5; Salami(Pizza pizza) {
this.pizza = pizza;
} @Override
public String getSummary() {
return pizza.getSummary() +
", with salami (" + PRICE + ")";
} @Override
public int getPrice() {
return pizza.getPrice() + PRICE;
}
}
And the client code would be like this:
Pizza pizza = new StandardPizza(); // standard pizza, base: 25
pizza = new Salami(pizza); // salami added, total: 30
pizza = new Corn(pizza); // corn added, total: 34
pizza = new Corn(pizza); // corn x2 added, total: 38Output:
Standard Pizza (25), with salami (5), with corn (4), with corn (4)
Price: 38Pizza pizza = new ThinCrustPizza(); // thin crust pizza, base: 30
pizza = new Mushroom(pizza); // mushroom added, total: 36
pizza = new Salami(pizza); // salami added, total: 41Output:
Thin Crust Pizza (30), with mushroom (6), with salami (5)
Price: 41
See, it’s all very reusable! If one day you want to add a new crust/ingredient or update the prices of current ones, you won’t need to touch the client code.
Each class will only care of its own. (Single Responsibility Principle)
Real-Life Example:
Decorator Pattern is widely used in java.io package, such as:
InputStream in = new FileInputStream(file);
in = new BufferedInputStream(in);
in = new DataInputStream(in);
FileInputStream is the crust type here, while BufferedInputStream and DataInputStream are the ingredients.
You can even write your own decorator to your program by simply extending the java.io package decorators’ superclass (FilterInputStream) and wrap it around the base class FileInputStream.
public class UpperCaseInputStream extends FilterInputStream {
public UpperCaseInputStream(InputStream in) {
super(in);
} @Override
public int read() throws IOException {
return Character.toUpperCase(super.read());
}
}
And the client code would be like this:
InputStream in = new FileInputStream(file);
in = new UpperCaseInputStream(in);// other decorators
In short, Decorator Pattern helps us to dynamically attach additional functionalities to our base class as we write code.
To read it as a single story:
Decorator Pattern — A Quick Guide (4 min read)
3- Observer Pattern
“Observer Pattern defines one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically.”
Aside from formal definition, Observer Pattern helps us to listen (subscribe) to a subject and be up-to-date whenever a change occurs.
Subjects are open for the Observers to subscribe, and does not know (nor care about) how they are implemented and vice versa.
Simple Example:
Suppose you are building a social media app and implementing it for multiple platforms.
a platform enum (for the sake of simplicity)
public enum Platform {
WEB(1),
MOBILE(2);
}
a view interface that has a display method
public interface View {
void display();
}
a subject interface that has add/delete observer and notify observers methods
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
an observer interface that has an update method
public interface Observer {
void update(Double x, Double y);
}
Let’s say you want to show currency information in our app. Based on incoming data, you’ll update the numbers on the screen.
a currency class that implements the methods in Subject interface
public class CurrencyData implements Subject {
private final List<Observer> observers = new ArrayList<>();
private Double value;
private Double interestRate; @Override
public void registerObserver(Observer observer) {
observers.add(observer);
} @Override
public void removeObserver(Observer observer) {
observers.remove(observer);
} @Override
public void notifyObservers() {
observers.forEach(observer ->
observer.update(value, interestRate));
} public void setValue(Double value) {
this.value = value;
notifyObservers();
} public void setInterestRate(Double interestRate) {
this.interestRate = interestRate;
notifyObservers();
}// getters}
Notice how notifyObservers() is called whenever a change occurs.
As your app grows larger, you may want to add new functionalities, such as weather information.
a weather class that implements the methods in Subject interface
public class WeatherData implements Subject {
private final List<Observer> observers = new ArrayList<>();
private Double temperature;
private Double humidity; @Override
public void registerObserver(Observer observer) {
observers.add(observer);
} @Override
public void removeObserver(Observer observer) {
observers.remove(observer);
} @Override
public void notifyObservers() {
observers.forEach(observer ->
observer.update(temperature, humidity));
} public void setTemperature(Double temperature) {
this.temperature = temperature;
notifyObservers();
} public void setHumidity(Double humidity) {
this.humidity = humidity;
notifyObservers();
}// getters}
a currency display class that implements the methods in Observer and View interfaces
public class CurrencyDisplay implements Observer, View {
private Platform platform;
private Double value;
private Double interestRate; public CurrencyDisplay(Platform platform, Subject subject) {
this.platform = platform;
subject.registerObserver(this);
} public void unsubscribe(Subject subject) {
subject.removeObserver(this);
} @Override
public void update(Double temperature, Double humidity) {
this.value = temperature;
this.interestRate = humidity;
display();
} @Override
public void display() {
// display
}
}
a weather display class that implements the methods in Observer and View interfaces
public class WeatherDisplay implements Observer, View {
private Platform platform;
private Double temperature;
private Double humidity; public WeatherDisplay(Platform platform, Subject subject) {
this.platform = platform;
subject.registerObserver(this);
} public void unsubscribe(Subject subject) {
subject.removeObserver(this);
} @Override
public void update(Double temperature, Double humidity) {
this.temperature = temperature;
this.humidity = humidity;
display();
} @Override
public void display() {
// display
}
}
Let’s see it in action:
CurrencyData currency = new CurrencyData();
WeatherData weather = new WeatherData();CurrencyDisplay webCurrency = new CurrencyDisplay(WEB, currency);
CurrencyDisplay mobileCurrency
= new CurrencyDisplay(MOBILE, currency);
// currency information is available for both platformsWeatherDisplay mobileWeather = new WeatherDisplay(MOBILE, weather);
// weather information is available for mobile,
// not available for webdouble temperature = 21.05;
weather.setTemperature(temperature);
// for weather -> only mobile is notifieddouble interestRate = 14.18;
currency.setInterestRate(interestRate);
// for currency -> both web and mobile got notifiedmobileCurrency.unsubscribe(currency);
// for currency -> mobile is not listening anymoreinterestRate = 15.20;
currency.setInterestRate(interestRate);
// for currency -> only web is notified
In Observer Pattern, the Subject is an open book. Observer is free to subscribe and unsubscribe based on its needs.
If you want to unsubscribe, it’s so easy! Since this design is loosely coupled, you can just call unsubscribe(subject) and it’s all done!
Loosely coupled designs allow us to build flexible object-oriented systems that can handle change. They minimize the interdependency between objects.
Real-Life Example:
Observer Pattern is widely used in javax.swing package.
Let’s think of a service that will update both the displays and the database information based on change of data:
UserData data = new UserData();Display webDisplay = new Display(WEB, data);
Display mobileDisplay = new Display(MOBILE, data);Database postgresql = new Postgresql(data);
Database couchbase = new Couchbase(data);// web display, mobile display, postgresql and couchbase
// all subscribed to user data.
Button is the subject here, when the button is clicked, data gets changed.
button.addActionListener((ActionListener)
ae -> data.setEmail(email));
When data gets changed, all observers get notified and updated.
To read it as a single story:
Observer Pattern — A Quick Guide (4 min read)
4- Factory Pattern
“There are 3 types of Factory Pattern: Simple, Method and Abstract.”
Simple Factory
Suppose you have a local pizza shop and you instantiate a pizza object for every order you get.
Pizza pizza = new Pizza();
Instead of that, you can simply do this:
public class Pizza {
private Pizza(){} // hide the constructor, so no one
initializes pizza with "new" public static Pizza create() {
return new Pizza();
}
}
or this:
public class PizzaFactory {
public static Pizza create() {
return new Pizza();
}
}public class Pizza {
// assuming your models are in the same package, you can
make this package-private to make sure constructor won't be
called from client code
protected Pizza(){}
}
and the client code will be like this:
Pizza pizza = Pizza.create();orPizza pizza = PizzaFactory.create();
If you’d like to change how this method works (or maybe add some business logic to it), you can update create instead of putting the logic code to the constructor.
Don’t fret, I hear you, you say we could achieve the same thing with a constructor.
Well, Simple Factory is basically a constructor that has a custom name.
Think of it like this:
Person person = new Person(170);toPerson person = Person.fromHeight(170);orPerson person = PersonFactory.fromHeight(170);
It’s up to you to use it or not. I personally like it.
Some people don’t even count Simple Factory as a Design Pattern, though.
Factory Method and Abstract Factory on the other hand, is a bit different.
So let’s get to Factory Method.
Factory Method
So… back to the order management system.
Let’s say your business is larger now and you start to sell burgers.
Factory Method helps you to select between pizza and burger by overriding a method but keeps non-varying parts in a common method.
Let’s first create a Meal interface with meals implementing it.
public interface Meal {
// implementations
}public class Pizza implements Meal {
// implementations
}public class Burger implements Meal {
// implementations
}
Let’s make a more generic MealFactory interface with both PizzaFactory and BurgerFactory implementing it.
interface MealFactory {
//factory method
Meal create(); // compulsory inheritance
}public class PizzaFactory implements MealFactory {
// implementations @Override
Meal create() {
return new Pizza();
}
}public class BurgerFactory implements MealFactory {
// implementations
@Override
Meal create() {
return new Burger();
}
}
Both pizza types override create according their own needs.
Now client code would be like:
MealFactory pizzaFactory = new PizzaFactory();
Meal pizza = pizzaFactory.create();MealFactory burgerFactory = new BurgerFactory();
Meal burger = burgerFactory.create();
That’s it!
If both Meals have a common method regardless of their type, you can turn MealFactory to abstract class instead of an interface.
abstract class MealFactory {
//factory method
protected abstact Meal create(); // compulsory inheritance public void createAndSendOrder() {
Meal meal = create();
// do stuff
}
}public class PizzaFactory extends MealFactory {
// implementations @Override
protected Meal create() {
return new Pizza();
}
}public class BurgerFactory extends MealFactory {
// implementations
@Override
protected Meal create() {
return new Burger();
}
}
And the client code would be like:
MealFactory pizzaFactory = new PizzaFactory();
pizzaFactory.createAndSendOrder();MealFactory burgerFactory = new BurgerFactory();
burgerFactory.createAndSendOrder();
It’s great because I also encapsulated the creation logic from client completely.
Abstract Factory
Now let’s add vegan options of pizza and burger.
So it will be like:
interface MealFactory {
Pizza createPizza(); // no inheritance needed
Burger createBurger(); // no inheritance needed
}public class VeganMealFactory implements MealFactory { @Override
public Pizza createPizza() {
return new VeganPizza();
} @Override
public Burger createBurger() {
return new VeganBurger();
}
}public class NonVeganMealFactory implements MealFactory { @Override
public Pizza createPizza() {
return new NonVeganPizza();
} @Override
public Burger createBurger() {
return new NonVeganBurger();
}
}
Now it’s a factory of factories.
In Factory Method, only a single method was responsible of creation of a pizza, here we have separate methods for separate pizzas, so whole class is responsible.
In Factory Method,Pizza and Burger needed to be in type Meal,
here it’s not compulsory.
Let’s see the client code:
MealFactory veganMealFactory = new VeganMealFactory();
MealFactory nonVeganMealFactory = new NonVeganMealFactory();Pizza veganPizza = veganMealFactory.createPizza();
Burger veganBurger = veganMealFactory.createBurger();Pizza nonVeganPizza = nonVeganMealFactory.createPizza();
Burger nonVeganBurger = nonVeganMealFactory.createBurger();
Since both VeganMealFactory and NonVeganMealFactory are of type PizzaFactory, can’t we just decide between them using a simple decider?
Let’s try it:
public class MealFactoryDecider { private MealFactoryDecider(){} public static MealFactory decide(MealType mealType) {
switch (mealType) {
case VEGAN:
return new VeganMealFactory();
case NONVEGAN:
return new NonVeganMealFactory();
default: throw new RuntimeException("Invalid type.");
}
}
}
And the final client code would be like:
MealFactory veganMealFactory =
MealFactoryDecider.decide(VEGAN);MealFactory nonVeganMealFactory = MealFactoryDecider.decide(NONVEGAN);Pizza veganPizza = veganMealFactory.createPizza();
Burger veganBurger = veganMealFactory.createBurger();Pizza nonVeganPizza = nonVeganMealFactory.createPizza();
Burger nonVeganBurger = nonVeganMealFactory.createBurger();
Remember to combine multiple patterns if you feel like it’ll be a good fit.
Wrap-Up
Simple Factory relies on readability: while it may not be considered a design pattern, it’s a simple way to decouple your clients from concrete classes.
Factory Method relies on inheritance: object creation is delegated to subclasses which implement the specific factory method to create objects.
Abstract Factory relies on composition: object creation is implemented in multiple methods exposed in the factory interface.
All factory patterns promote loose coupling by reducing the dependency of your application on concrete classes.
To read it as a single story:
Factory Pattern — A Quick Guide (4 min read)
Simple Example:
Suppose you need a carpenter for a sofa, chairs and a cupboard.
It’d be the best if you call a one carpenter and do all the work with him.
It wouldn’t be wise to call a different carpenter for each object.
Real-Life Example:
It’s very similar to Simple Example. Let’s say you need a database client that you’ll handle all the CRUD operations.
You wouldn’t want to have multiple database clients since it will cause unexpected behaviors such as memory issues and data corruption.
Implementation
public class Singleton {private static Singleton instance;
private Singleton(){} // hiding the constructor
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
// other methods
}
And the client code is:
Singleton singleton = Singleton.getInstance();
In the above implementation, the logic is such as:
if object does not exist → create and return object
if object exists → return already created object
Dealing with Multithreading
The problem with above code block is that in a multithreaded application objects might get initialized multiple times if threads access the same if block concurrently when getInstance() is used for the first time.
Let’s continue with possible solutions:
1. Eager Initialization
public class Singleton {private static Singleton instance = new Singleton();
private Singleton(){} // hiding the constructor
public static Singleton getInstance() {
return instance;
}
// other methods
}
Using this approach, we pass the responsibility to JVM and it initializes our Singleton instance when the class is loaded, so multithreading is not an issue.
You can use this if you are sure you’ll use getInstance() at least one time in your application.
2. Locking the Initialization
public class Singleton {private static Singleton instance;
private Singleton(){} // hiding the constructor
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
// other methods
}
We can lock the getInstance() by using synchronized keyword and force other threads to wait current thread to exit before entering this code block.
While the above example is correct (and used in most examples), this is a bad practice. We don’t need to lock the method every time getInstance() is called, we need to lock it the first time it’s being called. Since locking is a performance-wise expensive operation, we had better not lock whole method.
public class Singleton {private static volatile Singleton instance;
private Singleton(){} // hiding the constructor
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
// other methods
}
Things to Consider
While Singleton Pattern is great and widely being used, it’s sometimes considered as an anti-pattern since it can help us mask bad design.
1. Violating loose-coupling
When we pass Singleton classes around like Singleton.getInstance(),
we may not realize the classes of our app know too much about each other, being too tightly-coupled.
For example passing the database client all over our app is a bad practice.
2. Violating Single Responsibility Principle
Singleton Pattern both handles ensuring a class has only one instance and providing an access point to it, which makes it carry two huge responsibilities on its shoulders.
3. Singleton Pattern vs Static Classes
We can make Singleton lazy-initialized with our if check above, static methods are lazy-initialized by default.
Singleton allows method overriding, static methods don’t allow overriding but can achieve similar behavior by method hiding.
When to use static class: If you are planning to write helper methods such as math, array manipulations etc., it can be easier to just use a class full of static helper methods.
When to use Singleton: Rest of everything. Singleton Class is just a normal class following normal OOP principles.
4. Singleton Pattern vs Dependency Injection
If you are already using a framework that handles injections, it may be a better practice to just inject classes instead of applying Singleton Pattern.
I prefer to first check if it’s a helper method, if yes, create a static class.
If not, check our DI availabilities. If yes, use DI. If not, use Singleton.
To read it as a single story:
Singleton Pattern — A Quick Guide (4 min read)
6- Builder Pattern
“Builder Pattern helps us construct complex objects by allowing creation of different representations of that object using same construction code.”
Builder Pattern is just a simple alternative of telescoping constructors and
many lines of setters.
public class Pizza { private int slices;
private boolean cheese;
private boolean mushrooms;public Pizza(int slices) { … }
public Pizza(int slices, boolean cheese) { … }
public Pizza(int slices, boolean cheese, boolean mushrooms) { … }// implement methods}
So this is what a telescoping constructor look like.
We can extend it even more:
public Pizza(int slices, boolean cheese, boolean mushrooms, boolean pepperoni, boolean onions, boolean studentDiscount…) { … }
Assuming slices is a required parameter and the rest of them is not,
it wouldn’t be wise to create a different constructor for each optional field to satisfy all object combinations.
Simplest way to get rid of this would be to just create a constructor which takes slice parameter and sets rest of the fields upon our needs.
public class Pizza {private int slices;
private boolean cheese;
private boolean mushrooms;public Pizza(int slices) { … }}
And the client code would be like:
Pizza pizza = new Pizza(2);pizza.setCheese(true);
pizza.setMushrooms(true);
pizza.setPepperoni(true);
…
This alternative doesn’t look good either.
The easiest way to save this code is to make a builder class, such as:
public class Pizza {
private int slices;
private boolean cheese;
private boolean mushrooms;
private boolean pepperoni;
private boolean onions;
private boolean studentDiscount; public Pizza(Builder builder) {
this.slices = builder.slices;
this.cheese = builder.cheese;
this.mushrooms = builder.mushrooms;
this.pepperoni = builder.pepperoni;
this.onions = builder.onions;
this.studentDiscount = builder.studentDiscount;
}// getters & setterspublic static final class Builder {
private int slices;
private boolean cheese;
private boolean mushrooms;
private boolean pepperoni;
private boolean onions;
private boolean studentDiscount; private Builder() {} public static Builder initialize(int slices) {
return new Builder().withSlices(slices);
} public Builder withSlices(int slices) {
this.slices = slices;
return this;
} public Builder withCheese() {
this.cheese = true;
return this;
} public Builder withMushrooms() {
this.mushrooms = true;
return this;
} public Builder withPepperoni() {
this.pepperoni = true;
return this;
} public Builder withOnions() {
this.onions = true;
return this;
} public Builder withStudentDiscount() {
this.studentDiscount = true;
return this;
} public Pizza build() {
return new Pizza(this);
}
}
}
Since slices is a required field, Builder’s private constructor is hidden and has a simple factory method that only permits to initialize with slices.
Up until build() method is called, Builder returns a Builder type,
build() method transforms Pizza.Builder to an actual Pizza object.
And the client code would be like:
Pizza pizza = Pizza.Builder
.initialize(2)
.withCheese()
.withMushrooms()
.withStudentDiscount()
.build();
To read it as a single story:
Builder Pattern — A Quick Guide (2 min read)
7- Command Pattern
“Command Pattern encapsulates a request as an object, helping us to use it with different requests and support undo operation.”
Command Pattern is like ordering at a restaurant.
Chef does not hear orders from the client itself, he/she uses a helper.
(like a waiter/waitress)
Suppose you have an air conditioner and you need to turn it on/off.
Normally in this use case, you would simply write an AirConditioner class with turnOn() and turnOff() methods, but this time we’ll implement the actions in classes instead of methods.
To do this, you can write a helper class such as RemoteControl.
Now it’s like this:
We are the client, remote control is the waiter/waitress and AC is the chef.
Let’s see it in code:
public class AirConditioner {
private boolean active; void turnOn() {
active = true;
} void turnOff() {
active = false;
}
}
You need separate classes for on/off actions (commands), such as:
public interface Command {
void execute();
void undo();
}public class AirConditionerOff implements Command {
private final AirConditioner airConditioner; public AirConditionerOff(AirConditioner airConditioner) {
this.airConditioner = airConditioner;
} @Override
public void execute() {
airConditioner.turnOff();
} @Override
public void undo() {
airConditioner.turnOn();
}
}public class AirConditionerOn implements Command {
private final AirConditioner airConditioner; public AirConditionerOn(AirConditioner airConditioner) {
this.airConditioner = airConditioner;
} @Override
public void execute() {
airConditioner.turnOn();
} @Override
public void undo() {
airConditioner.turnOff();
}
}
And you need a helper (invoker) to communicate between us and AC:
public class RemoteControl {private RemoteControl(){} // hiding the constructor public static void submit(Command command) {
command.execute();
} public static void undo(Command command) {
command.undo();
}
}
And the client code would be like:
AirConditioner airConditioner = new AirConditioner();Command turnOn = new AirConditionerOn(airConditioner);
Command turnOff = new AirConditionerOff(airConditioner);RemoteControl.submit(turnOn); // turned air conditioner on
RemoteControl.undo(turnOn); // turned air conditioner offRemoteControl.submit(turnOn); // turned air conditioner on
RemoteControl.submit(turnOff); // turned air conditioner off
Multiple Commands & Stack
What if you want to create an app with multiple commands, not just on/off?
public class AirConditioner {
private int power; void off() {
power = Power.OFF; // OFF = 0
} void low() {
power = Power.LOW; // LOW = 1
} void medium() {
power = Power.MEDIUM; // MEDIUM = 2
} void high() {
power = Power.HIGH; // HIGH = 3
}
}public interface Command {
void execute();
// undo is gone from here for now, we'll see it below later}public class AirConditionerOff implements Command {
private final AirConditioner airConditioner; public AirConditionerOff(AirConditioner airConditioner) {
this.airConditioner = airConditioner;
} @Override
public void execute() {
airConditioner.off();
}
}public class AirConditionerLowPower implements Command {
private final AirConditioner airConditioner; public AirConditionerLowPower(AirConditioner airConditioner) {
this.airConditioner = airConditioner;
} @Override
public void execute() {
airConditioner.low();
}
}public class AirConditionerMediumPower implements Command {
private final AirConditioner airConditioner; public AirConditionerMediumPower(AirConditioner airConditioner){
this.airConditioner = airConditioner;
} @Override
public void execute() {
airConditioner.medium();
}
}public class AirConditionerHighPower implements Command {
private final AirConditioner airConditioner; public AirConditionerHighPower(AirConditioner airConditioner){
this.airConditioner = airConditioner;
} @Override
public void execute() {
airConditioner.high();
}
}
But this time undo operation is a bit more complicated. Since now there are multiple commands, you need to know the exact steps client took.
To achieve this you can use Stack data structure.
public class RemoteControl { private RemoteControl() {} private static final Stack<Command> undoStack = new Stack<>();
private static final Stack<Command> redoStack = new Stack<>(); public static void submit(Command command) {
command.execute(); undoStack.add(command);
redoStack.clear();
} public static void undo() {
if (!undoStack.isEmpty()) {
undoStack.peek().execute();
redoStack.add(undoStack.pop());
}
} public static void redo() {
if (!redoStack.isEmpty()) {
redoStack.peek().execute();
undoStack.add(redoStack.pop());
}
}
}
Now we should be able to use multiple commands and also support undo/redo operation. :)
Let’s see it in client code:
AirConditioner airConditioner = new AirConditioner();Command highPower = new AirConditionerHighPower(airConditioner);
Command mediumPower = new AirConditionerMediumPower(airConditioner);
Command lowPower = new AirConditionerLowPower(airConditioner);
Command off = new AirConditionerOff(airConditioner);// feel free to apply any creational design pattern aboveRemoteControl.submit(highPower); // air conditioner set HIGH
RemoteControl.submit(lowPower); // air conditioner set LOW
RemoteControl.submit(mediumPower); // air conditioner set MEDIUMRemoteControl.undo(); // air conditioner set LOW
RemoteControl.undo(); // air conditioner set HIGHRemoteControl.redo(); // air conditioner set LOWRemoteControl.submit(off); // air conditioner set OFF
To read it as a single story:
Command Pattern — A Quick Guide (4 min read)
8- Adapter Pattern
“Adapter Pattern converts the interface of a class into another interface the clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.”
Simple Example:
Suppose you are traveling from Europe to USA, since there are different outlets being used, you need an adapter/wrapper/converter for you to be able to use your devices.
Real-Life Example:
Suppose you are writing an app and you are using a Kafka client to consume messages from a topic.
This client has methods called consume and produce to read and write messages to a Kafka topic and the broker you are listening the topic from has version 1.8.
Let’s call this: Client A.
Now you need to listen to an another topic from another broker.
This broker has version 2.4 and your Kafka client does not support this version yet. You find another Kafka client to help you.
Let’s call this: Client B.
But there is a problem: You wrote all your code according to Client A but
Client B uses different convention.
For example, it uses the name read instead of consume and uses the name write instead of produce.
You don’t want to migrate all your code to Client B, because you’ve heard that Client A will soon start supporting newer versions and Client B overall uses more memory than Client A.
Since our code should be open for extension, but closed for modification, the best thing to do there is to not touch existing code and just wrap Client A’s code around Client B to work with existing logic.
There may be lots of other similar scenarios to use Adapter Pattern, it’s a very common need to convert types to each other.
Implementation
public class ClientBAdapter extends ClientA {
private final ClientB clientB; public ClientBAdapter(ClientB clientB) {
this.clientB = clientB;
} @Override
public void consume() {
clientB.read();
} @Override
public void produce() {
clientB.write();
}
}
With this approach, ClientB can easily act like ClientA.
ClientB clientB = new ClientB();clientB.write();
clientB.read();ClientA clientBAdapted = new ClientBAdapter(clientB);
// feel free to use a static helper method instead of a constructorclientBAdapted.consume();
clientBAdapted.produce();
We didn’t change any existing code, just wrote one helper class.
Simple and a quick solution. :)
Facade and Adapter both wrap multiple classes, but a Facade’s intent is to simplify, while an Adapter’s is to convert the interface to something different.
To read it as a single story:
Adapter Pattern — A Quick Guide (2 min read)
9- Facade Pattern
“Facade Pattern provides a unified interface that is easier to use for sets of interfaces in a subsytem.”
Simple Example:
Suppose you are going to build a house and there are lots of steps to achieve this. You need to build walls and a roof, buy furnitures, paint the walls etc…
You may just want to paint the walls as standalone feature in the future,
but it would be good to have an all-in-one package for building a house, as we need a complete house right now.
Real-Life Example:
Suppose you are writing an app and you need to make HTTP requests.
You need to tweak it with different variations of configurations according to your needs.
It would be best if you do all the configurations inside a method and just call that method from client code for simplified usage.
Implementation
public class HttpClientFacade {
private final HttpClient httpClient; public HttpClientFacade(HttpClient httpClient) {
this.httpClient = httpClient;
} public JsonObject getRequest(String url, String agentName) {
httpClient.setConnectTimeout(5000);
httpClient.expectCompression(true);
httpClient.setHeader("agentname", agentName);
//...
//...
return httpClient.get(url);
}
}
When you need to add/remove a property during GET request, you just need to implement it into this method and it’ll be a quick and an easy change.
Other parts of the code don’t need to know the details of getRequest.
And the client code would be like:
HttpClient httpClient = new HttpClient();
HttpClientFacade httpClientFacade =
new HttpClientFacade(httpClient);httpClientFacade.getRequest("emretanriverdi.medium.com", "itsme");
Facade and Adapter both wrap multiple classes, but a Facade’s intent is to simplify, while an Adapter’s is to convert the interface to something different.
To read it as a single story:
Facade Pattern — A Quick Guide (2 min read)
10- State Pattern
“State Pattern lets an object alter its behavior when its internal state changes.”
Simple Example:
Suppose you have a cell phone and it has three profiles for incoming calls:
Silent mode, vibrating mode and ringing mode.
Incoming call is always the same but how you receive it always changes.
Real-Life Example:
Suppose you have a website that has Homepage, About and Contact pages.
It’s internal logic for dark mode & light mode is the same.
It would be the best if we could change the visibility with just a simple code block, keeping it abstract.
Implementation
public interface PageViewState {
void view();
}public class ViewStateContext {
private PageViewState currentState; public ViewStateContext() {
currentState = new LightModeState(); // default
} public void setState(PageViewState state) {
currentState = state;
} public void view() {
currentState.view(this);
}
}public class LightModeState implements PageState {
@Override
public void view(ViewStateContext ctx) {
// implementation
}
}public class DarkModeState implements PageState {
@Override
public void view(ViewStateContext ctx) {
// implementation
}
}
And the client code would be like:
ViewStateContext stateContext = new ViewStateContext();stateContext.view(); // shows pages in light mode
stateContext.view();stateContext.setState(new DarkModeState());stateContext.view(); // shows pages in dark mode
stateContext.view();
Voila!
To read it as a single story:
State Pattern — A Quick Guide (2 min read)
11- Chain of Responsibility Pattern
“Chain of Responsibility Pattern lets you pass requests along a chain of handlers. Upon receiving a request, each handler decides either to process the request or to pass it to the next handler in the chain.”
Simple Example:
Suppose you want to play a video game on your computer and the game is trying to choose the optimal settings based on your GPU.
It checks if your GPU can handle Ultra, if not;
it checks if your GPU can handle High, if not;
it checks if your GPU can handle Medium.
And if you are lucky enough, it stops there.
If your GPU still isn’t enough, it checks if you can handle Low,
then Ultra Low (if the game has this option).
If your GPU still isn’t enough, the game simply won’t work on your computer.
Chain of Responsibility Pattern is actually as simple as this.
Real-Life Example:
Up until now you must have realized this is all bunch of if & else if statements.
public void initialize(VisualEntity visualEntity,
FrameRequest frameRequest) {
initializeCommonFields(visualEntity, frameRequest); if (frameRequest.isCircle()) {
// initialize circle
} else if (frameRequest.isSquare()) {
// initialize square
} else if (frameRequest.isRectangle()) {
// initialize rectangle
} else throw new UnsupportedOperationException("message");
}
Let’s say you have a code block like this where you let your client choose a frame for their photo.
I believe we can agree that all these if blocks don’t look very beautiful and if one day you want to add Triangle to your available frames, or maybe a Heart symbol for couples, the code block would get even larger.
So let’s try a different design with Chain of Responsibility!
Implementation
public abstract class InitializerSelector {
private InitializerSelector successor; abstract void initialize(VisualEntity visualEntity,
FrameRequest frameRequest); public InitializerSelector getSuccessor() {
return successor;
} public void setSuccessor(InitializerSelector successor) {
this.successor = successor;
}
}public class CircleInitializer extends InitializerSelector { public CircleInitializer(SquareInitializer squareInitializer) {
setSuccessor(squareInitializer);
} @Override
public void initialize(VisualEntity visualEntity,
FrameRequest frameRequest) {
if (frameRequest.isCircle()) {
// initialize circle
} else {
getSuccessor().initialize(visualEntity, frameRequest);
}
}
}public class SquareInitializer extends InitializerSelector { public SquareInitializer(RectangleInitializer
rectangleInitializer) {
setSuccessor(rectangleInitializer);
} @Override
public void initialize(VisualEntity visualEntity,
FrameRequest frameRequest) {
if (frameRequest.isSquare()) {
// initialize square
} else {
getSuccessor().initialize(visualEntity, frameRequest);
}
}
}public class RectangleInitializer extends InitializerSelector {
@Override
public void initialize(VisualEntity visualEntity,
FrameRequest frameRequest) {
if (frameRequest.isRectangle()) {
// initialize rectangle
} else {
throw new UnsupportedOperationException("message");
}
}
}
If one day you need to add TriangleInitializer, you can add it as a successor of RectangleInitializer and move the Exception to TriangleInitializer.
And the client code would be like:
public void initialize(VisualEntity visualEntity,
FrameRequest frameRequest) {
initializeCommonFields(visualEntity, frameRequest);
circleInitializer.initialize(visualEntity, frameRequest);
}
You can finalize the client code by changing the initial call from circleInitializer to something more abstract, to not reveal logic to client code. You can create a new class which will start the flow by calling CircleInitializer.
public void initialize(VisualEntity visualEntity,
FrameRequest frameRequest) {
initializeCommonFields(visualEntity, frameRequest);
frameInitializer.initialize(visualEntity, frameRequest);
}
There are many other ways to implement the Chain of Responsibility Pattern and Spring has its own built-in way to implement it even easier, feel free to use different approaches based on your situation (and framework choice).
To read it as a single story:
Chain of Responsibility Pattern — A Quick Guide (3 min read)
Example:
Let’s say you have an application that helps you deploy your applications to Staging or Production Environments, like this:
public class StagingDeploy {
public void deploy() {
runUnitTests();
pushSemanticVersioningTag();
pushTaggedImage();
saveImageToRegistryAsBackup();
} void runUnitTests() {
// implementation for unit tests
} void pushSemanticVersioningTag() {
// implementation for semantic versioning
} void pushTaggedImageToStaging() {
// implementation for pushing tagged image to staging
}
void saveImageToRegistryAsBackup() {
// implementation for saving image as backup
}
}public class ProductionDeploy {
public void deploy() {
runSmokeTests();
pushReleaseTag();
pushTaggedImage();
saveImageToRegistryAsBackup();
} void runSmokeTests() {
// implementation for smoke tests
} void pushReleaseTag() {
// implementation for release versioning
} void pushTaggedImageToProduction() {
// implementation for pushing tagged image to production
} void saveImageToRegistryAsBackup() {
// implementation for saving image as backup
}
}
There are clear similarities between Staging and Production deploys, so you can create a template for both of them to use.
Implementation
public abstract class Deploy {
public void final deploy() {
runTests();
pushTag();
pushTaggedImage();
saveImage();
} abstract void runTests(); abstract void pushTag(); abstract void pushTaggedImage(); abstract void saveImage();
}
and now you can extend it like this:
public class StagingDeploy extends Deploy { @Override
void runTests() {
// implementation for unit tests
} @Override
void pushTag() {
// implementation for semantic versioning
} @Override
void pushTaggedImage() {
// implementation for pushing tagged image to staging
} @Override
void saveImage() {
// implementation for saving image as backup
}
}public class ProductionDeploy extends Deploy { @Override
void runTests() {
// implementation for smoke tests
} @Override
void pushTag() {
// implementation for release versioning
} @Override
void pushTaggedImage() {
// implementation for pushing tagged image to production
} @Override
void saveImage() {
// implementation for saving image as backup
}
}
And the client code would be like:
StagingDeploy stagingDeploy = new StagingDeploy();
stagingDeploy.deploy();ProductionDeploy productionDeploy = new ProductionDeploy();
productionDeploy.deploy();
For this case, you may have realized saveImage() method is the same for both classes. So you can change the code like this:
public abstract class Deploy {
public void final deploy() {
runTests();
pushTag();
pushTaggedImage();
saveImage();
} abstract void runTests(); abstract void pushTag(); abstract void pushTaggedImage(); void saveImage() {
// implementation for saving image as backup
}}public class StagingDeploy extends Deploy { @Override
void runTests() {
// implementation for unit tests
} @Override
void pushTag() {
// implementation for semantic versioning
} @Override
void pushTaggedImage() {
// implementation for pushing tagged image to staging
}
}public class ProductionDeploy extends Deploy { @Override
void runTests() {
// implementation for smoke tests
} @Override
void pushTag() {
// implementation for release versioning
} @Override
void pushTaggedImage() {
// implementation for pushing tagged image to production
}
}
Hooks
There is also another concept called Hooks.
Hooks are methods in abstract classes that can be overriden, but not have to.
Suppose you want to add “performance tests” step to our template but you don’t want it to run in Staging and Production environments.
Let’s say you now have a new environment that crucial apps work and you want to make sure the applications deployed here runs faster than 2ms.
public abstract class Deploy {
public void final deploy() {
runPerformanceTests();
runTests();
pushTag();
pushTaggedImage();
saveImage();
} void runPerformanceTests() {
// empty
} abstract void runTests(); abstract void pushTag(); abstract void pushTaggedImage(); void saveImage() {
// implementation for saving image as backup
}
}public class StagingDeploy extends Deploy { @Override
void runTests() {
// implementation for unit tests
} @Override
void pushTag() {
// implementation for semantic versioning
} @Override
void pushTaggedImage() {
// implementation for pushing tagged image to staging
}
}public class ProductionDeploy extends Deploy { @Override
void runTests() {
// implementation for smoke tests
} @Override
void pushTag() {
// implementation for release versioning
} @Override
void pushTaggedImage() {
// implementation for pushing tagged image to production
}
}public class NewProductionDeploy extends Deploy { @Override
void runPerformanceTests() {
// implementation for performance tests
} @Override
void runTests() {
// implementation for both unit & smoke tests
} @Override
void pushTag() {
// implementation for release versioning
} @Override
void pushTaggedImage() {
// implementation for pushing tagged image to new production
}
}
Hooks: You CAN override the method to add behavior, but you don’t have to.
Abstract Methods: You MUST override the method.
Things to Consider
While Template Method Pattern is great and widely being used (sometimes without even realizing), we should think twice before using it:
1. Always favor composition over inheritance.
Before using Template Method Pattern, check if Strategy Pattern could be a better fit. Template Method Pattern works at the class level, so it’s a bit more static. Strategy Pattern works on the object level, letting you switch behaviors at runtime.
2. Be aware that you may violate the Liskov Substitution Principle.
LSP basically says that objects of a superclass should be replaceable with objects of its subclasses without breaking the application. While it sounds easy, as the application gets larger in the future, we may unknowingly modify the template to not be suitable for all subclasses.
3. Be aware that you may violate the Open-Closed Principle.
OCD basically says that classes should be open for extension, but closed for modification. As classes get larger, maintaining the template for each class becomes a problem and instead of classes adapting themselves to the template, things turn around and template starts to adapt itself to classes.
To read it as a single story:
Template Method Pattern — A Quick Guide (4 min read)
Thank you for reading!
Reference(s)
- Head First Design Patterns. by Eric Freeman, Elisabeth Robson, Bert Bates, Kathy Sierra. Released October 2004. Publisher(s): O’Reilly Media, Inc.
- Dive into Design Patterns. by Alexander Shvets, Released 2019.