Design Patterns
Solutions to common problems in software design
https://www.youtube.com/watch?v=tv-_1er1mWI
https://refactoring.guru/design-patterns/history
Why Design Patterns are important?
-
Design patterns are a toolkit of tried and tested solutions to common problems in software design. Even if you never encounter these problems, knowing patterns is still useful because it teaches you how to solve all sorts of problems using principles of object-oriented design.
-
Design patterns define a common language that you and your teammates can use to communicate more efficiently. You can say, “Oh, just use a Singleton for that,” and everyone will understand the idea behind your suggestion.
All patterns can be categorized by their purpose:
-
Creational patterns provide object creation mechanisms that increase flexibility and reuse of existing code.
-
Structural patterns explain how to assemble objects and classes into larger structures, while keeping these structures flexible and efficient.
-
Behavioral patterns take care of effective communication and the assignment of responsibilities between objects.
Creational patterns
Structural patterns
Behavioral patterns
- Chain of Responsibility
- Command
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor
Factory Method
Factory Method is a creational design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created.
Factory methods can be recognized by creation methods that construct objects from concrete classes. While concrete classes are used during the object creation, the return type of the factory methods is usually declared as either an abstract class or an interface.
Abstract Factory
Abstract Factory is a creational design pattern that lets you produce families of related objects without specifying their concrete classes.
Builder
Builder is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code.
The Builder pattern lets you construct products step-by-step. You could defer execution of some steps without breaking the final product. You can even call steps recursively, which comes in handy when you need to build an object tree.
A builder doesn’t expose the unfinished product while running construction steps. This prevents the client code from fetching an incomplete result.
Prototype
Prototype is a creational design pattern that lets you copy existing objects without making your code dependent on their classes.
Use the Prototype pattern when your code shouldn’t depend on the concrete classes of objects that you need to copy.
Singleton
Singleton is a creational design pattern that lets you ensure that a class has only one instance, while providing a global access point to this instance.
-
Make the default constructor private, to prevent other objects from using the new operator with the Singleton class.
-
Create a static creation method that acts as a constructor. Under the hood, this method calls the private constructor to create an object and saves it in a static field. All following calls to this method return the cached object.
This class doesn’t have a public constructor, so the only way to get its object is to call the getInstance method. This method caches the first created object and returns it in all subsequent calls.
Adapter
Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate.
The Adapter pattern lets you create a middle-layer class that serves as a translator between your code and a legacy class, a 3rd-party class or any other class with a weird interface.
Bridge
Say you have a geometric Shape class with a pair of subclasses: Circle and Square. You want to extend this class hierarchy to incorporate colors, so you plan to create Red and Blue shape subclasses. However, since you already have two subclasses, you’ll need to create four class combinations such as BlueCircle and RedSquare.
Adding new shape types and colors to the hierarchy will grow it exponentially. For example, to add a triangle shape you’d need to introduce two subclasses, one for each color. And after that, adding a new color would require creating three subclasses, one for each shape type. The further we go, the worse it becomes.
The Bridge pattern attempts to solve this problem by switching from inheritance to the object composition. What this means is that you extract one of the dimensions into a separate class hierarchy, so that the original classes will reference an object of the new hierarchy, instead of having all of its state and behaviors within one class.
Following this approach, we can extract the color-related code into its own class with two subclasses: Red
and Blue
. The Shape class then gets a reference field pointing to one of the color objects. Now the shape can delegate any color-related work to the linked color object. That reference will act as a bridge between the Shape
and Color
classes. From now on, adding new colors won’t require changing the shape hierarchy, and vice versa.
Composite
Composite is a structural design pattern that lets you compose objects into tree structures and then work with these structures as if they were individual objects.
Imagine that you have two types of objects: Products and Boxes. A Box can contain several Products as well as a number of smaller Boxes. These little Boxes can also hold some Products or even smaller Boxes, and so on.
The Composite pattern suggests that you work with Products and Boxes through a common interface which declares a method for calculating the total price.
How would this method work? For a product, it’d simply return the product’s price. For a box, it’d go over each item the box contains, ask its price and then return a total for this box. If one of these items were a smaller box, that box would also start going over its contents and so on, until the prices of all inner components were calculated. A box could even add some extra cost to the final price, such as packaging cost.
Decorator
Decorator is a structural design pattern that lets you attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors.
Wearing clothes is an example of using decorators. When you’re cold, you wrap yourself in a sweater. If you’re still cold with a sweater, you can wear a jacket on top. If it’s raining, you can put on a raincoat. All of these garments “extend” your basic behavior but aren’t part of you, and you can easily take off any piece of clothing whenever you don’t need it.
Facade
Facade is a structural design pattern that provides a simplified interface to a library, a framework, or any other complex set of classes.
When you call a shop to place a phone order, an operator is your facade to all services and departments of the shop. The operator provides you with a simple voice interface to the ordering system, payment gateways, and various delivery services.
For example, let’s return to our video conversion framework. It can be broken down into two layers: video- and audio-related. For each layer, you can create a facade and then make the classes of each layer communicate with each other via those facades.
Flyweight
Flyweight is a structural design pattern that lets you fit more objects into the available amount of RAM by sharing common parts of state between multiple objects instead of keeping all of the data in each object.
The benefit of applying the pattern depends heavily on how and where it’s used. It’s most useful when:
-
an application needs to spawn a huge number of similar objects
-
this drains all available RAM on a target device
-
the objects contain duplicate states which can be extracted and shared between multiple objects
Example: Vast quantities of bullets, missiles, and shrapnel from explosions should fly all over the map.
Proxy
Proxy is a structural design pattern that lets you provide a substitute or placeholder for another object. A proxy controls access to the original object, allowing you to perform something either before or after the request gets through to the original object.
Chain of Responsibility
Chain of Responsibility is a behavioral design pattern that lets you pass requests along a chain of handlers. Upon receiving a request, each handler decides either to process the request or to pass it to the next handler in the chain.
The Chain of Responsibility relies on transforming particular behaviors into stand-alone objects called handlers
A handler can decide not to pass the request further down the chain and effectively stop any further processing.
Use the pattern when it’s essential to execute several handlers in a particular order.