Design Pattern is a term used for a general, reusable solution to a commonly occurring problem in software design. It is important to understand that Design Patterns are not finished pieces of code that can be transformed and directly applied to your software. Rather they are more like a template, a description, or a concept that can give you an idea of how to solve a problem that can be used in many different situations.
They can incredibly useful when used in the right places and for the right reasons. When used strategically, they enhance your software and make it more efficient by methods, already refined by a myriad of developers.
Why Should You Use A Design Pattern?
Using Design Patterns in software development has several benefits:
- They are proven solutions: Design patterns are often used by many different developers, so you can be sure that they work as intended. Furthermore, you can be confident that they were revised multiple times and optimization has taken place already.
- They are easily reusable: Design patterns document a reusable solution that can be modified to solve multiple particular problems because they are not tied to a specific problem.
- They are expressive: Large solutions can be explained elegantly by Design Patterns.
- They ease communication: Many developers are familiar with Design Patterns and use them easily to communicate about possible solutions to given problems with each other.
- They prevent the need for refactoring code: Often if you use Design Patterns while developing software you could avoid refactoring it later. This only applies if you use the correct one for a given problem because it describes already an optimal solution.
- They lower the size of the codebase: Because Design Patterns are usually elegant, optimized, and well-documented solutions they require less code than other solutions.
Where It All Began
In 1994 an iconic computer science book "Design Patterns: Elements of Reusable Object-Oriented Software" was first published by four authors: Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. In technology circles, you'll often see these authors named "The Gang of Four" shortened to GoF.
Normally, as a developer that creates small to enterprise-class applications, you will encounter the Gang of Four Design Patterns on a daily basis.
The 23 GoF Design Patterns
The 23 Gang of Four Design Patterns are divided into three categories:
- Creational: For handling Object creation mechanisms. They are used to create objects, rather than having to instantiate objects directly. Your program will be more flexible in deciding which object it creates for a specific use case.
- Structural: For identifying ways to realize relationships between objects. This concerns class/object composition with inheritance to define different ways to gain new functionality.
- Behavioral: For handling communication between different objects.
There are 5 Design Patterns in the creational category designed by the Gang of Four.
1. Abstract Factory: Creates an instance of several families of classes without detailing concrete classes. It allows the creation of a factory for factory classes.
2. Builder: Separates object construction from its representation, and always creates the same type of object. It creates an object step by step and a method to finally get the object instance.
3. Factory Method: Moves the responsibility of instantiating an object from the class to a Factory. The Factory then makes an instance of several derived classes based on interfaced data or events.
4. Prototype: This pattern provides a mechanism to copy or clone the original object to a new object and then modify it according to your needs to avoid costly object creation.
5. Singleton: Restricts the initialization of a class to ensure that only one instance of the class can be created. Multiple initialization attempts will result in returning the same object over and over again.
There are 7 Design Patterns in the structural category designed by the Gang of Four.
1. Adapter: Provides an interface between two unrelated entities in a way that they both can work together. It wraps an interface between already existing classes to enable working with each other without modifying the source code directly.
2. Bridge: Defines a strategy to decouple the interfaces from implementation and hide the implementation details from the client program. So both can vary independently. It uses encapsulation, aggregation, and inheritance to separate responsibilities into multiple classes.
3. Composite: This pattern is used if you have to implement a part-whole hierarchy. It creates a group of objects that should be treated equally as a single instance of one object. It composes zero-or-more objects in a way that they can be manipulated as one single object.
4. Decorator: Is used for modifying the behavior of objects during runtime. It dynamically adds alternate processing to objects without affecting other objects from the same class.
5. Facade: Provides a unified interface to a set of interfaces in a subsystem. It defines a higher level that helps to easier use of the subsystem. It masks the underlying complex or structural code and serves as a "front-facing interface"
6. Flyweight: Describes a fine-grained instance used for the efficient sharing of information. It caches and reuses object instances, used with immutable objects to minimize memory usage.
7. Proxy: Provides a placeholder for another object. You should use it to control access, reduce cost, and reduce complexity.
There are 11 Design Patterns in the behavioral category designed by the Gang of Four.
1. Chain of Responsibility: Describes a technique to delegate commands or pass requests between a chain of objects to find the object that can handle the command or request.
2. Command: This pattern encapsulates a command request as an object to enable, log, and/or queue requests. Furthermore, it provides error handling for unhandled command requests,
3. Interpreter: Includes language-specific elements in the application to match the grammar of the intended language. It defines a grammatical representation of a language and the interpreter that works with this grammar.
4. Iterator: Describes a standardized way to traverse through a group of objects. It accesses the containing objects sequentially without exposing their underlying representation.
5. Mediator: Provides centralized communication between different objects in a system to enable loose coupling between different objects. It encapsulates how these different objects interact with each other.
6. Memento: Stores a state of an object whenever it is saved to enable restoring previous states by providing undo operations.
7. Observer: Notifies state changes to a set of subscribing classes to ensure consistency between all of them.
8. State: Enables behavioral changes based on an internal state of an object. If the state of an object changes it also changes how it reacts to requests.
9. Strategy: This pattern is used when multiple algorithms for a specific task exist and a client decides which implementation should be used at runtime. It encapsulates the algorithm inside the class separating selection from implementation.
10. Template Method: Defines an abstract skeleton of an algorithm in such a way that subclasses have to implement the concrete behavior.
11. Visitor: Separates an algorithm from an object structure on which it operates. This enables adding new operations to existing object structures without changing the structure.
Now that you have read this short introduction to software Design Patterns, you should have an idea about several important techniques used in nearly every programming language.
You should now hone your skills by implementing all or at least some of these Design Patterns into your side projects or just as practice. Also, you can start to contemplate how to apply them in your real projects.
Although, there are several implementations that benefit from utilizing Design Patterns keep in mind that they were never meant to be hacked together shortcuts to be applied in a 'one-size-fits-all' manner to your code. There is no perfect substitute for a genuine problem-solving ability in software engineering.
It is paramount to understand, Design Patterns provide a common language to conceptualize repeating problems and solutions while working with a team or managing large code bases.
That being said, the most important caveat is ensuring that the how and why behind each Design Pattern are fully understood by the developer.
If you enjoyed reading this article consider commenting your valuable thoughts in the comments section. I would love to hear your feedback about all the Design patterns I mentioned here. Furthermore, share this article with your friends and colleagues to also help them to know about Design Patterns.