What are design patterns and why are they useful?
TL;DR
Design patterns are named, reusable design approaches to recurring problems and tradeoffs. They are communication tools and starting points, not code templates or guarantees of maintainability, performance, or scale. Apply one when the problem and forces are actually present; a direct function, object, or module is often clearer than introducing pattern-shaped abstractions preemptively.
What are design patterns and why are they useful?
Definition of design patterns
Design patterns are general, reusable solutions to common problems that occur in software design. They are not finished designs that can be directly transformed into code but rather templates that describe how to solve a problem in various contexts. The concept was popularized by the book "Design Patterns: Elements of Reusable Object-Oriented Software" by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, also known as the "Gang of Four" (GoF).
Types of design patterns
Design patterns are typically categorized into three main types:
- Creational patterns: Deal with object creation mechanisms, trying to create objects in a manner suitable to the situation. Examples include Singleton, Factory Method, and Abstract Factory.
- Structural patterns: Deal with object composition or the structure of classes and objects. Examples include Adapter, Composite, and Decorator.
- Behavioral patterns: Deal with object interaction and responsibility. Examples include Observer, Strategy, and Command.
Why design patterns are useful
- Reusability: Design patterns provide a proven solution to common problems, which can be reused across different projects.
- Maintainability: They help in writing code that is easier to understand, maintain, and extend.
- Scalability: Design patterns can help in designing systems that are scalable and can handle growth in complexity.
- Communication: They provide a common vocabulary for developers, making it easier to discuss and share design ideas.
- Tradeoff awareness: A pattern gives teams a known set of consequences and alternatives to discuss.
Example: Singleton pattern
The Singleton pattern ensures that a class has only one instance and provides a global point of access to it. Here is a simple implementation in JavaScript:
class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;}someMethod() {console.log('Singleton method');}}const instance1 = new Singleton();const instance2 = new Singleton();console.log(instance1 === instance2); // true
In this example, the Singleton class ensures that only one instance of the class is created. Any subsequent calls to create a new instance will return the existing instance.
The example also shows why patterns need judgment: returning an existing object from a constructor is surprising, hides shared mutable state, and only enforces uniqueness in that JavaScript realm. A normal module export or explicit dependency may be simpler. Evaluate a pattern by the problem it solves, its failure modes, and how easily the team can test and change it.
Further reading
- Design Patterns: Elements of Reusable Object-Oriented Software by the Gang of Four
- Refactoring Guru: Design Patterns
- JavaScript Design Patterns by Addy Osmani