DESIGN PATTERNS

James Nwankwo
6 min readJan 30, 2021

Design patterns can be defined as regular solutions used by software engineers to resolve frequent design problems in object-oriented programming. They are guidelines not standard rules which an engineer can customize and implement to fix a design flaw in their project. These solutions arose by virtue of trial and error by very many different software engineers over a considerable stretch of time. The best practices were extracted from the failures and then named, and classified for other engineers to implement in the resolution of their own problems.

Design pattern was initiated by four authors (the gang of four) named: Erich Gamma, John Vlissides, Ralph Johnson, and Richard Helm who published the acclaimed book “Design Patterns: Elements of Reusable Object-Oriented Software.” According to the gang of four, design patterns are primarily based on the following principles of object-oriented design:

  • Program to an interface and not the implementation,
  • Favor object composition over inheritance.

They were inspired by Christopher Alexander — the author of “A Pattern Language: Towns, Buildings, Construction.”

There are two main uses of design patterns in software engineering. They are:

  • They provide developers with a set of familiar, standard terminology for them to employ in the discussion and dissection of their code methods and processes,
  • They are an encyclopedia of best practices which have been tested and certified as real solutions to design flaws in software development.

There are over twenty-three distinct types of design patterns and they can be classified into three major classes. They are:

1. Creational patterns: These describe patterns that create or instantiate objects. They are attentive to the way in which objects are created as they try to reduce complexity by instantiating objects in a controlled manner, thus, increasing flexibility and reuse of existing code. Examples of this pattern are: factory method, singleton, abstract factory, builder, prototype.

2. Structural patterns: These describe how objects and classes are composed and combined to form larger objects. They explain how to assemble objects and classes into larger structures while keeping them flexible, efficient, and small. They commonly employ inheritance and interface principles of OOP. Examples are: MVC, Adapter, Bridge, MVVM, Composite and Façade.

3. Behavioral patterns: They describe how objects communicate with each other. They identify common communication patterns between objects and are concerned with the assignment of responsibilities between them. They influence how state and behavior flow through a system by optimizing how state and behavior are transferred and modified. Examples include: strategy, memento, mediator, observer, and delegation.

SINGLETON

The singleton design pattern is a creational pattern that ensures only one instance exists for a given class and that there is a global access point to that singular instance. It aims to keep a lid on the initialization of objects of a class by virtue of the single instance and ensuring that each subsequent call to the instance returns a single particular object. Singletons may often be modeled as a server within the application that accepts requests to send, store, or retrieve data and configure the resource state.

There is also the singleton-plus pattern which provides a shared or default singleton instance, but it also allows other instances to be created. The singleton plus is used when a shared instance is useful while allowing for possible custom instances e.g file managers which have a default instance- singleton, or you can create your own.

The singleton pattern is commonly used when having more than one instance of a class will cause problems. It controls access to shared resources and is commonly used in databases. The implementation of a singleton has two steps:

  1. Make the default constructor private to prevent other objects from using the new operator with the singleton class,
  2. Create a static creation method that acts as a constructor. To ensure there is only one instance of the singleton. It has to be impossible for another instance to be created and marking the initializers as private does the trick.

How to Implement:

  • Create a singleton class to manage your data
  • Add a private static field to the class for storing the instance
  • Implement lazy initialization inside the static method- lazy init creates an object on its first call, and then stores it
  • Make the constructor of the class private and then replace all direct calls to the singleton’s constructor

Advantages

  • You can be sure that a class has only a single instance
  • You gain a global access point to that instance
  • The object is initialized only when it is requested for the first time and then it is stored and can be called upon anytime

Disadvantages

  • Singleton hinder unit testing: If the object and methods associated with a singleton become tightly coupled it becomes impossible to test without writing a fully functional class dedicated to it
  • Singletons create hidden dependencies: Given that it is available throughout the code, it can be overused and since it is not also transparent while passing to different methods, it becomes difficult to track.
  • Singleton violates the single responsibility principle as it solves two problems at the same time

STRATEGY

Strategy is a behavioral design pattern that lets you define an interchangeable family of objects from which the required process is selected at runtime. This family of objects can be switched or set-up at runtime. The strategy pattern posits that you take a class that does something specific in many different ways, and extract all of these qualities into separate classes called strategies. The original class — called context — contains a field for storing reference to one of the strategies and it delegates the work to a linked strategy object instead of executing it on its own. The context is encapsulated from other strategies and knows little about them as it is not responsible for selecting an appropriate object for the job, as the client passes the desired strategy to the context.

The strategy pattern is comprised of three parts:

  • The object using a strategy: This is usually the view controller and most times it is, even though technically; it can be any kind of object that requires interchangeable behavior
  • The strategy protocol: this defines methods that every strategy must implement and,
  • The strategies: These are objects that conform to the strategy protocol.

When to Use

This pattern should be used when;

  1. You have two or more different behavior that are interchangeable
  2. Flexibility is required. Strategy relies on a protocol instead of concrete objects for increased flexibility, any object that implements a strategy protocol can be used as a strategy at runtime
  3. The objects are intended to change at runtime. The strategy pattern defines a family of objects and they are easily changeable at runtime as they are intended to be changeable unlike delegates
  4. To add functionalities to a class without extending it. The strategy object is about using one object to get another object to do something hence it is a behavioral design pattern. Be careful about pre-optimizing or overusing strategy pattern
  5. If the behavior will not change, it can be put inside the consuming view controller

How to Implement

  1. Identify an algorithm in the context class that is likely to change a lot
  2. Declare the strategy interface common to all variants of the algorithm
  3. Extract all the algorithms into their own classes and make them implement the strategy interface
  4. In the context class, add a field for storing a reference to a strategy object. Provide a setter for replacing values of the field.
  5. Clients of the context must associate it with a suitable strategy that matches the way they expect the context to perform its primary job.

Advantages

  1. You can swap algorithms used inside an object at runtime
  2. This pattern adheres to the open/closed principle as it is open to extension (introducing new strategies without changing the context) but closed to modification
  3. It is easy to avoid conditionals, thus, it leads to clean code
  4. Your adapters can be tested with ease as they are isolated. Unit testing is easier too
  5. You can replace inheritance with composition reducing inheritance. A change in spec can be resolved easier with composition

Disadvantages

  1. Clients have to be aware about the different strategies as they are responsible for selecting the right one
  2. Because each specific policy class will produce a new class, it will increase the number of classes the system needs to maintain.
  3. Too many objects in the system.

--

--