Design Patterns - A Cheatsheet

Design Patterns - A Cheatsheet
Photo by UX Indonesia / Unsplash

This is a short note curated by me for fast reference of design patterns. All content is derived from the book "Design Patterns For Dummies" by Steve Holzner, PhD

The Mediator Pattern

You can use a mediator here to encapsulate all the navigation code out of the separate pages and place it into a mediator object instead. From then on, each page just has to report any change of state to the mediator, and the mediator knows what page to send the user to.

Adapter Pattern

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

Proxy Pattern

A proxy is a stand-in for another object that makes the local code think it’s dealing with a local object. Behind the scenes, the proxy connects to the remote object, all the while making the local code believe it’s working with a local object

Observer Pattern

The Observer design pattern is about passing notifications around to update a set of objects when some important event has occurred. You can add new observer objects at runtime and remove them as needed. When an event occurs, all registered observers are notified.

The big four OOP building blocks

  • Abstraction: Abstraction is all about breaking your approach to a problem into natural segments. This is where you come up with the objects that divide the problem into manageable parts.
  • Encapsulating: hide the complexities inside objects and then create a simple interface to let that object interact with the rest of your code.
  • polymorphism: perform various actions on the shapes you’re working with — and then decide on the actual shape(s) you want to use at runtime. Polymorphic (which means many form) code works with any such shape without being rewritten.
  • Inheritance: the process by which one class can inherit methods and properties from another.
💡
Design pattern-oriented programming often prefers object composition over inheritance.
💡
Separate the parts of your code that will change the most from the rest of your application and try to make them as freestanding as possible for easy maintenance. You should also always try to reuse those parts as much as possible.

With inheritance, base classes and derived classes have an “is-a” relationship.

Composition Example:

public interface GoAlgorithm {
	public void go();
}

public class GoByFlying implements GoAlgorithm {
public void go() {
System.out.println(“Now I’m flying.”);
}}
public abstract class Vehicle {
private GoAlgorithm goAlgorithm;
public Vehicle() {
}
public void setGoAlgorithm (GoAlgorithm algorithm) {
goAlgorithm = algorithm; }
public void go() { goAlgorithm.go();
}
}
public class Helicopter extends Vehicle {
public Helicopter() {
setGoAlgorithm(new GoByFlyingAlgorithm()); }
}

Advantage: Can change behavior at runtime

Strategy Pattern

The Strategy design pattern says that you should extract the volatile parts of your code and encapsulate them as objects; you can use those objects as you need them

At runtime, you just use polymorphism to choose the object(s) you want to work with

“Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.”

Decorator Pattern

“Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.”

💡
As much as possible, make your code closed for modification, but open for extension. In other words, design your core code so that it doesn’t have to be modified a lot, but may be extended as needed.

Example

public class Computer {
public Computer() {
}
public String description() {
return “computer”; }
}
public abstract class ComponentDecorator extends Computer {
public abstract String description();
}

public class Disk extends ComponentDecorator {
Computer computer;
public Disk(Computer c) {
computer = c; }
public String description() {
return computer.description() + “ and a disk”; }}
public class Test {
public static void main(String args[]) {
Computer computer = new Computer();
computer = new Disk(computer); computer = new Monitor(computer); computer = new CD(computer); computer = new CD(computer);
System.out.println(“You’re getting a “ + computer.description() + “.”);
}
}

Factory Pattern

Define an interface for creating an object, but let subclasses decide which class to instantiate. The factory method lets a class defer instantiation to subclasses

Example

public abstract class ConnectionFactory {
public ConnectionFactory() {
}
protected abstract Connection createConnection(String type);
}
public class SecureFactory extends ConnectionFactory {
public Connection createConnection(String type) {
if (type.equals(“Oracle”)){
return new SecureOracleConnection();
}
else if (type.equals(“SQL Server”)){
return new SecureSqlServerConnection(); }
else {
return new SecureMySqlConnection();
}
}
}

Observer Pattern

Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically

There’s a subject and observers. Observers can be registered or unregistered on the subject. The subject sends notifications to all registered observers.

Interfaces

public interface Subject {
public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObservers();
}
public interface Observer {
public void update(String operation, String record);
}

Chain of Responsibility Pattern

Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.

Example

interface HelpInterface {
public void getHelp(int helpConstant);
}
public class FrontEnd implements HelpInterface {
final int FRONT_END_HELP = 1;
HelpInterface successor;
public FrontEnd(HelpInterface s) {
successor = s; }
public void getHelp(int helpConstant) {
if(helpConstant != FRONT_END_HELP){ successor.getHelp(helpConstant);
} else {
System.out.println(“This is the front end. Don’t you like it?”);
} }}
public class IntermediateLayer implements HelpInterface {
final int INTERMEDIATE_LAYER_HELP = 2;
HelpInterface successor;
public IntermediateLayer(HelpInterface s) {
successor = s; }
public void getHelp(int helpConstant) {
if(helpConstant != INTERMEDIATE_LAYER_HELP){ successor.getHelp(helpConstant);
} else {
System.out.println(“This is the intermediate layer. Nice, eh?”);
} }
}
public class Application implements HelpInterface {
public Application() {
}
public void getHelp(int helpConstant) {
System.out.println(“This is the MegaGigaCo application.”); }
}
public class TestHelp {
public static void main(String args[]) {
final int FRONT_END_HELP = 1;
final int INTERMEDIATE_LAYER_HELP = 2; final int GENERAL_HELP = 3;
Application app = new Application();
IntermediateLayer intermediateLayer = new IntermediateLayer(app);
FrontEnd frontEnd = new FrontEnd(intermediateLayer);
frontEnd.getHelp(GENERAL_HELP);
}
}

Singleton Pattern

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

You use the Singleton design pattern when you want to either restrict resource use (instead of creating numbers of large objects without limit) or when you have a sensitive object whose data shouldn’t be accessed by multiple instances (such as a registry).

Private constructor and public static access point to an instance

Flyweight Pattern

Flyweight pattern must, “Use sharing to support large numbers of fine-grained objects efficiently.”

A flyweight is a shared object that can be used in multiple contexts simultaneously. The fly-weight acts as an independent object in each context it’s indistinguishable from an instance of the object that’s not shared.

Facade Pattern

It’s fundamentally a design issue — if an object or class interface is too hard to work with, the Facade pattern gives you a front end to that interface to make it easier.

“Provide a unified interface to a set of interfaces in a system. Facade defines a higher-level interface that makes the subsystem easier to use.”

💡
Principle of Least Knowledge/ Law of Demeter/ effective encapsulation you don’t want to insist that separate entities (classes of objects) have to know too much about each other. As much as possible, you should lock away the details inside each class or object and make the coupling between entities as loose as possible

Example

public class SimpleProductFacade {
DifficultProduct difficultProduct;
public SimpleProductFacade() {
difficultProduct = new DifficultProduct(); }
public void setName(String n) {
char chars[] = n.toCharArray();
if(chars.length > 0){ difficultProduct.setFirstNameCharacter(chars[0]);
}
.
.
.
if(chars.length > 6){
difficultProduct.setSeventhNameCharacter(chars[6]); }
}
public String getName() {
return difficultProduct.getName(); }
}

Template Method pattern

Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. The Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.

“executes a multi-step algorithm that’s customizable by subclasses”

Example

public abstract class RobotTemplate {
public final void go() {
start(); getParts(); assemble(); test(); stop();
}
. . .
}
public class AutomotiveRobot extends RobotTemplate {
public void getParts() {
System.out.println(“Getting a carburetor....”); }
public void assemble() {
System.out.println(“Installing the carburetor....”); }
public void test() {
System.out.println(“Revving the engine....”); }
}

Builder Pattern

Separate the construction of a complex object from its representation so that the same construction processes can create different representations.

The main difference between the Template Method and the Builder design patterns is in who creates the sequence of steps in the algorithm.

Iterator Pattern

Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

Composite Pattern

The Composite pattern is all about creating tree-like structures where the leaves in a structure can be treated in the same way as the branches (which are substructures that can contain multiple leaves, as well as other branches).

“Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.”

use an abstract class as the basis for both the leaves and branches in the tree

State Pattern

💡
When you face a large-scale application and the coding gets out of hand, it often helps to start thinking in terms of various states. This device helps you segment your code into independent units (states) — ideally, each state should be logically independent of the others, so thinking in terms of states can automatically divide your code into discrete sections.

Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.