A software design pattern is a reusable solution to solve a common problem when designing an application.
Design patterns supports object-oriented programming (OOP) that is based on the concepts of both objects (instances of a class; data with unique attributes) and classes (user-defined types of data).
There are 23 classic design patterns that can be broken down into three types, organized by their intent into creational design patterns, structural design patterns, and behavioral design patterns.
1. Creational Design Patterns
Factory Method: Creates objects with a common interface and lets a class defer instantiation to subclasses.
Abstract Factory: Creates a family of related objects.
Builder: A step-by-step pattern for creating complex objects, separating construction and representation.
Prototype: Supports the copying of existing objects without code becoming dependent on classes.
Singleton: Restricts object creation for a class to only one instance.
2. Structural Design Patterns
Adapter: How to change or adapt an interface to that of another existing class to allow incompatible interfaces to work together.
Bridge: A method to decouple an interface from its implementation.
Composite: Leverages a tree structure to support manipulation as one object.
Decorator: Dynamically extends (adds or overrides) functionality.
Façade: Defines a high-level interface to simplify the use of a large body of code.
Flyweight: Minimize memory use by sharing data with similar objects.
Proxy: How to represent an object with another object to enable access control, reduce cost and reduce complexity.
3. Behavioral Design Patterns
Chain of Responsibility: A method for commands to be delegated to a chain of processing objects.
Command: Encapsulates a command request in an object.
Interpreter: Supports the use of language elements within an application.
Iterator: Supports iterative (sequential) access to collection elements.
Mediator: Articulates simple communication between classes.
Memento: A process to save and restore the internal/original state of an object.
Observer: Defines how to notify objects of changes to other object(s).
State: How to alter the behavior of an object when its stage changes.
Strategy: Encapsulates an algorithm inside a class.
Visitor: Defines a new operation on a class without making changes to the class.
Template Method: Defines the skeleton of an operation while allowing subclasses to refine certain steps.
Why Do We Need Design Patterns?
Design patterns provide a best practice approach to support object-oriented software design, which is easier to design, implement, change, test and reuse. These design patterns provide best practices and structures.
1. Proven Solution
Design patterns provide a proven, reliable solution to a common problem, meaning the software developer does not have to “reinvent the wheel” when that problem occurs.
2. Reusable
Design patterns can be modified to solve many kinds of problems – they are not just tied to a single problem.
3. Expressive
Design patterns are an elegant solution.
4. Prevent the Need for Refactoring Code
Since the design pattern is already the optimal solution for the problem, this can avoid refactoring.
5. Lower the Size of the Codebase
Each pattern helps software developers change how the system works without a full redesign. Further, as the “optimal” solution, the design pattern often requires less code.
7 Best Software Design Patterns
Although there are 23 design patterns listed in Design Patterns: Elements of Reusable Object-Oriented Software, of those there are 7 that are considered the most influential or important. This section outlines the 7 best software design patterns, why they are important, and when to use them.
1. Singleton Design Pattern
The singleton design pattern falls under the “creational” type, restricting object creation for a class to only one instance and providing global access to a global variable. Ensures a class only has one instance, like a print spooler in an office network. For example, many web developers lock up the “sitemap” to a single version that has global scope. Further, other patterns such as factory method, builder, prototype can use singletons. Facade and state objects are also often singletons.
While you may only have or need one instance of a class, this does not necessarily mean that is the time to use a singleton pattern to lock that object up or to force it into a global state. Singletons are a controversial design pattern, with some even arguing that singletons are an antipattern to be avoided because locking up an object restricts future flexibility.
2. Factory Method Design Pattern
In the factory method, a “creation” design pattern, developers create objects with a common interface but allow a class defer instantiation to subclasses. It provides an interface for creating objects, allowing subclasses to decide which class to instantiate. Think of a standard hiring process for varying job roles. The factory method promotes loose coupling and code reuse, a “virtual constructor” that works with any class implementing the interface and allowing greater freedom for the sub-classes to choose the objects being created. When new classes are needed, they can be added to the factory.
The factory method is not appropriate for simple scenarios, an instance where developers risk over-complicating processes in order to use a design pattern.
3. Facade Design Pattern
The facade design pattern is a “structural” design pattern that helps provide one interface (class) for access to a large body of code / various objects. It simplifies interaction with a complex subsystem, like a customer service desk in a store. A facade hides complexities of various sub-systems (often organized into a class) with a simple interface. For example, an eCommerce customer only wants one point of interacting with a brand, rather than individually communicating (interfacing) with each system to support the sale such as product inventory, authentication, security, payment processing, order fulfillment, etc. In this case, the Facade has encapsulated all the “order” activities and systems to provide a single interface – the customer remains completely unaware of what’s going on behind the scenes. Facade is an important concept to support the loosely coupled microservices architecture.
4. Strategy Design Pattern
The strategy design pattern is a “behavioral” software design pattern that is sometimes known as a policy pattern. In the strategy pattern, interchangeable algorithms are encapsulated together into a “family” with one of the algorithms being selected at runtime as needed. For example, a family of algorithms may be related to “sorting” products in an eCommerce website – by size, colour, prize, etc. The strategy is implemented based upon the actions of the customer.
The strategy design pattern is incredibly powerful in personalization marketing strategies, responding to client location, inputs, or actions in order to deliver a different experience to each user.
5. Observer Design Pattern
The observer design pattern is “behavioral,” linking an object (subject) to dependents (observers) in a one-to-many pattern. When any of the observers change, the subject is notified. The observer design pattern is useful in any kind of event-driven programming such as notifying a user of a new comment on Facebook, sending an email when an item ships, etc.
6. Builder Design Pattern
The builder design pattern is “creational,” separating the object construction from the representation. This design pattern allows greater control over the design process (more a step-by-step), but it also decouples the representation to support multiple representations of an object using the same base construction code (the ConcreteBuilder step).
The builder pattern executes in sequential steps as the object is being built, calling only those steps that are necessary for each iteration of the object.
7. Adapter Design Pattern
The adapter design pattern is a “wrapper” that converts one kind of interface into another existing kind of interface. It connects interfaces that wouldn't otherwise work together, like a plug adapter. The adapter design pattern helps classes work together when they are incompatible, allowing code to work together. Adapter patterns are useful to take mixed interfaces and make them a consistent API.
These design patterns are like tools in your toolbox. Use them wisely to tackle most programming challenges.
Stay curious, keep learning.