CS 346 (W23)
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Design Patterns

A design pattern is a generalizable solution to a common problem that we’re attempting to address. Design patterns in software development are one case of a formalized best practice, specifically around how to structure your code to address specific, recurring design problems in software development.

We include design patterns in a discussion of software development because this is where they tend to be applied: they’re more detailed that architecture styles, but more abstract than source code. Often you will find that when you are working through high-level design, and describing the problem to address, you will recognize a problem as similar to something else that you’ve encoutered. A design pattern is a way to formalize that idea of a common, reusable solution, and give you a standard terminology to use when discussing this design with your peers.

Patterns originated with Christopher Alexander, an architect, in 19771. Design patterns in software gained popularity with the book Design Patterns: Elements of Reusable Object-Oriented Software, published in 1994 [Gamma 1994]. There have been many books and articles published since then, and during the early 2000s there was a strong push to expand Design Patterns and promote their use.

Design patterns have seen mixed-success. Some criticisms levelled:

  • They are not comprehensive, and do not reflect all styles of software or all problems encountered.
  • They are old-fashioned and do not reflect current software practices.
  • They add flexibility, at the cost of increased code complexity.

Broad criticisms are likely unfair. While it’s true that not all patterns are used, many of them are commonly used in professonal practice, and new patterns are being suggested. Design patterns certainly can add complexity to code, but they also encourage designs that help avoid subtle bugs later on.

In this section, we’ll outline the more common patterns, and indicate where they may be useful. The original set of patterns were subdivided based on the types of problems they addressed. We’ll examine a number of patterns in each category below: the original patterns are taken from Eric Gamma et al. 1994. Design Patterns: Elements of Reusable Object-Oriented Software. Examples and some explanations are from Alexander Shvets. 2019. Dive Into Design Patterns.

Creational Patterns

Creational Patterns control the dynamic creation of objects.

Pattern Description
Abstract Factory Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
Builder Separate the construction of a complex object from its representation, allowing the same construction process to create various representations.
Factory Method Pro­vide an inter­face for cre­at­ing objects in a super­class, but allows sub­class­es to alter the type of objects that will be created.
Prototype Specify the kinds of objects to create using a prototypical instance, and create new objects from the ‘skeleton’ of an existing object, thus boosting performance and keeping memory footprints to a minimum.
Singleton Ensure a class has only one instance, and provide a global point of access to it.

Example: Builder Pattern

Builder is a cre­ation­al design pat­tern that lets you con­struct com­plex objects step by step. The pat­tern allows you to pro­duce dif­fer­ent types and rep­re­sen­ta­tions of an object using the same con­struc­tion code.

Imagine that you have a class with a large number of variables that need to be specified when it is created. e.g. a house class, where you might have 15-20 different parameters to take into account, like style, floors, rooms, and so on. How would you model this?

You could create a single class to do this, but you would then need a huge constructor to take into account all of the different parameters.

  • You would then need to either provide a long parameter list, or call other methods to help set it up after it was instantiated (in which case you have construction code scattered around).
  • You could create subclasses, but then you have a potentially huge number of subclasses, some of which you may not actually use.

The builder pattern suggests that you put the object construction code into separate objects called builders. The pattern organizes construction into a series of steps. After calling the constructor, you call methods to invoke the steps in the correct order (and the object prevents calls until it is constructed). You only call the steps that you require, which are relevant to what you are building.

Builder Pattern

Even if you never utilize the Builder pattern directly, it’s used in a lot of complex Kotlin and Android libraries. e.g. the Alert dialogs in Android.

val dialog = AlertDialog.Builder(this)
	.setTitle("Title")
  .setIcon(R.mipmap.ic_launcher)
  .show()

Example: Singleton

Sin­gle­ton is a cre­ation­al design pat­tern that lets you ensure that a class has only one instance, while pro­vid­ing a glob­al access point to this instance.

Why is this pattern useful?

  1. Ensure that a class has just a sin­gle instance. The most com­mon rea­son for this is to con­trol access to some shared resource—for exam­ple, a data­base or a file.
  2. Pro­vide a glob­al access point to that instance. Just like a glob­al vari­able, the Sin­gle­ton pat­tern lets you access some object from any­where in the pro­gram. How­ev­er, it also pro­tects that instance from being over­writ­ten by other code.

All imple­men­ta­tions of the Sin­gle­ton have these two steps in common:

  1. Make the default con­struc­tor pri­vate, to pre­vent other objects from using the new oper­a­tor with the Sin­gle­ton class.

  2. Cre­ate a sta­t­ic cre­ation method that acts as a con­struc­tor.

In languages like Java, you would express the implementation in this way:

public class Singleton {

    private static Singleton instance = null;
    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

In Kotlin, it’s significantly easier.

object Singleton{   
    init {
        println("Singleton class invoked.")
    }
    fun print(){
        println("Print method called")
    }
}

fun main(args: Array<String>) {     
    Singleton.print()
    // echos "Print method called" to the screen
}

The object keyword in Kotlin creates an instance of a generic class. i.e. it’s instantiated automatically. Like any other class, you can add properties and methods if you wish.

Singletons are useful for times when you want a single, easily accessible instance of a class. e.g. Database object to access your database, Configuration object to store runtime parameters and so on. You should also consider it instead of extensively using global variables.

Structural Patterns

Structural Patterns are about organizing classes to form new structures.

Pattern Description
Adapter, Wrapper Convert the interface of a class into another interface clients expect. An adapter lets classes work together that could not otherwise because of incompatible interfaces.
Bridge Decouple an abstraction from its implementation allowing the two to vary independently.
Composite Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
Decorator Attach additional responsibilities to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing for extending functionality.
Proxy Provide a surrogate or placeholder for another object to control access to it.

Example: Adapter

Adapter is a struc­tur­al design pat­tern that allows objects with incom­pat­i­ble inter­faces to collaborate.

Imagine that you have a data source that is in XML, but you want to use a charting library that only consumes JSON data. You could try and extend one of those libraries to work with a different type of data, but that’s risky and may not even be possible if it’s a third-party library.

An adapter is an intermediate component that converts from one interface to another. In this case, it could handle the complexities of converting data between formats. Here’s a great example from Shvets (2019):

Adapter converting between data formats

The simplest way to implement this is using object com­po­si­tion: the adapter is a class that exposes an interface to the main application (client). The client makes calls using that interface, and the adapter performs necessary actions through the service (which is often a library, or something whose interface you cannot control).

Adapter classes

  1. The client is the class containing business logic (i.e. an application class that you control).
  2. The client interface describes the interface that you have designed for your application to communicate with that class.
  3. The service is some useful library or service (typically which is closed to you), which you want to leverage.
  4. The adapter is the class that you create to serve as an intermediary between these interfaces.
  5. The client application isn’t coupled to the adapter because it works through the client interface.

Behavioural Patterns

Behavioural Patterns are about identifying common communication patterns between objects.

Pattern Description
Command Encapsulate a request as an object, thereby allowing for the parameterization of clients with different requests, and the queuing or logging of requests. It also allows for the support of undoable operations.
Iterator Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
Memento Without violating encapsulation, capture and externalize an object’s internal state allowing the object to be restored to this state later.
Observer Define a one-to-many dependency between objects where a state change in one object results in all its dependents being notified and updated automatically.
Strategy Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
Visitor Represent an operation to be performed on the elements of an object structure. Visitor lets a new operation be defined without changing the classes of the elements on which it operates.

Example: Command

Com­mand is a behav­ioural design pat­tern that turns a request into a stand-alone object that con­tains all infor­ma­tion about the request (a command could also be thought of as an action to perform).

Imagine that you are writing a user interface, and you want to support a common action like Save. You might invoke Save from the menu, or a toolbar, or a button. Where do you put the code that actually handles saving the data?

If you attach it to the object that the user is interacting with, then you risk duplicating the code. e.g.

Different objects want to invoke the same code

The Command pattern suggests that you encapsulate the details of the command that you want executed into a separate request, which is then sent to the business logic layer of the application to process.

image-20220123105154963

The command class relationship to other classes:

Command Class

Example: Observer (MVC)

Observ­er is a behav­ioral design pat­tern that lets you define a sub­scrip­tion mech­a­nism to noti­fy mul­ti­ple objects about any events that hap­pen to the object they’re observing. This is also called publish-subscribe.

The object that has some inter­est­ing state is often called sub­ject, but since it’s also going to noti­fy other objects about the changes to its state, we’ll call it pub­lish­er. All other objects that want to track changes to the pub­lish­er’s state are called sub­scribers, or observers of the state of the publisher.

Subscribers register their interest in the subject, who adds them to an internal subscriber list. When something interest happens, the publisher notifies the subscribers through a provided interface.

Observer registration

Observers being notified

The subscribers can then react to the changes.

A modified version of Observer is the Model-View-Controller (MVC) pattern, which puts a third intermediate layer between the Publisher and Subscriber, which manages user input. That layer is not required for this pattern.

For more details on MVC, see the Building Applications section.


  1. “The Pattern of Streets,” JOURNAL OF THE AIP, September, 1977, Vol. 32, No. 3, pp. 273–278 ↩︎