Table of contents
1. What Is a Design Pattern?
A design pattern provides the solution to a commonly occurring problem in a software design. It isn’t a finished design that can directly be implemented on the code. It is a description or template for how to solve a problem that can be used in many different solutions.
Concepts to remember:
- A pattern is a solution to a problem in a context.
- The context is a situation in which the pattern applies, this should be a recurring situation.
- A problem refers to the goal we are typing to receive in the context, but it also refers to any
- constraints that occur in the context.
- The solution is what we are after, a general design that anyone can apply that resolves the goal and
- sets the constraints.
Object-oriented design patterns typically show relationships and interactions between classes or objects, without specifying the final application classes or objects that are involved. Patterns that imply
mutable state may be unsuited for functional programming languages. Some patterns can be rendered unnecessary in languages that have built-in support for solving the problem they are trying to solve, and object-oriented patterns are not necessarily suitable for non-object-oriented languages.
2. The Power of Design Pattern
Shared Pattern vocabularies are powerful. When we communicate with another developer of our team using pattern, we are communicating not just a pattern name but a whole set of qualities, characteristics, and constraints that a pattern represents.
Patterns allow us to say more with less. When we use a pattern in a description, other developers quickly know precisely the design we have in our mind.
Talking at the pattern level allows us to stay
in the design longer. Talking about the software system using patterns allows us to keep the discussion at the design level, without having a dive down to the minute details of implementing objects and classes.
Shared vocabularies can turbo-charge our development team. A team well-versed in design pattern can move more quickly with less room for misunderstanding.
3. How Do We Use Design Patterns?
Design Patterns don’t go directly into our code, they first go into our brain. Once we have loaded our brain with a good working knowledge of patterns, we can start to apply them to our new designs and rework our old code when we find it degrading into an inflexible mess.
4. Some of the Design Patterns
4.1. The Observer Pattern
The pattern that keeps our objects in the known when something they care about happens,
is the Observer pattern. It is one of the most commonly used design patterns and also incredibly useful.
The Observer pattern defines a one-to-many dependency between objects
s+o that when one object changes state, all of its dependents are notified and updated automatically.
The Subject and the Observer define the one-to-many relationship. We have one subject, who notifies many observers when something in the subject changes. The observers are dependent on the subject when the subject’s state changes, the observers are notified.
The Power of Loose Coupling
When two objects are loosely coupled, they can interact, but they typically have very little knowledge of each other.
- First, the only thing the subject knows about an observer is that it implements a certain interface. It doesn't need to know the concrete class of the observer, what it does, or anything else about it.
- We can add new Observers at any time.
- We never need to modify the Subject to add new types of Observer.
- We can reuse Subjects or Observers independently of each other.
- Changes to either the Subject or an Observer will not affect the other.
4.2 The Decorative Pattern
The decorator pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub-classing for extending functionality.
The Decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class. The decorator pattern is often useful for adhering to the
Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern. Decorator use can be more efficient than subclassing because an object's behavior can be augmented without defining an entirely new object.
What Problems Can It Solve?
- Responsibilities should be added to (and removed from) an object dynamically at run-time.
- A flexible alternative to subclassing for extending functionality should be provided.
4.3 The Factory Pattern
The Factory pattern defines an interface for creating an object, but lets subclasses decide which class to instantiate.
Factory method lets a class defer instantiation to subclasses.
As with every factory, the
Factory Method Pattern gives us a way to encapsulate the
Instantiations of concrete types. Looking at the class diagram above, we can see that the abstract creator class gives you an interface with a method for creating objects, also known as the
factory method. Any other methods implemented in the abstract creator are written to operate on products produced by the
factory method. Only subclasses actually implement the
factory method and create products.
Factory Method Pattern lets subclasses decide which class to instantiate. Because the creator class is written without the knowledge of the actual product that will be created, we say "decide" not because the pattern allows subclasses themselves to decide, but rather because the decision actually comes down to which subclass is used to create the product.
4.4 The Singleton Pattern
The Singleton pattern ensures a class has only one instance and provides a global point of access to it. This is useful when exactly one object is needed to coordinate actions across the system.
The singleton design pattern solves problems by allowing it to:
- Ensure that a class only has one instance
- Easily access the sole instance of a class
- Control its instantiation
- Restrict the number of instances
- Access a global variable
- The abstract factory, factory method, builder, and prototype patterns can use singletons.
- Facade objects are often singletons because only one facade object is required.
State objects are often singletons.
Singletons are often preferred to global variables because:
- They do not pollute the global namespace (or, in languages with nested namespaces, their containing namespace) with unnecessary variables.
- They permit lazy allocation and initialization, whereas global variables in many languages will always consume resources.
4.5 The Command Pattern
The Command pattern encapsulates a request as an object, thereby letting us parameterize other objects with different requests, queues, or log requests, and support undoable operations.
We know that a command object encapsulates a request by binding together a set of actions on a special receiver. To achieve this, it packages the action and receiver into an object that exposes just one method
execute(). When called,
execute() causes the actions to be invoked on the receiver. From the outside, no other object really knows what action gets performed on what receiver, they just know if they call the
execute method, their request will be serviced.
- GUI buttons and menu items
- Macro recording
- Mobile code
- Multi-level undo
- Parallel processing
- Progress bars
- Thread pools
These were some of the most useful design patterns. Try using them in your next app and let us know how it went.