Adapter (Adapter) is a structural design pattern designed to organize the use of the functions of an object that is not available for modification through a specially created interface.
The official definition is a bit tricky, but if you put it in your own words, an adapter is a design pattern that allows objects with incompatible interfaces to work together.
Used to organize the use of the functions of an object that is not available for modification through a specially created interface. An additional class is created that has the required interface, and this class in turn calls the methods of the desired object (which does not have the required interface).
Important! If in the code you meet the suffix Adapter for a class, then you have every right to consider that this class acts as an adapter and is associated with a group of classes that work according to the scheme described above.
It is used in cases where the system supports the required data and behavior, but has an inappropriate interface. The most common use of the Adapter pattern is when you want to create a class that inherits from a new or existing abstract class.
- The transition to using other external classes does not require reworking the system itself, it is enough to implement one more Adapter class.
- Independence from the implementation of external classes (classes from libraries whose code we cannot change). Your program becomes independent of the interface of external classes.
Decorator is a structural design pattern for dynamically attaching additional behavior to an object. The Decorator pattern provides a good and flexible alternative to the practice of subclassing to extend functionality.
Used to dynamically connect additional obligations to an object.
Many of you will ask: how can you dynamically (while the program is running) add new behavior to an object? An object can be assembled from pieces, that is, small objects. Remember filter chains in servlets? Or the Stream API when you wrote a query using filter(), map(), list()?
IntStream.of(50, 60, 70, 80, 90).filter(x -> x < 90).map(x -> x + 10).limit(3).forEach(System.out::print);
Strengths of the Decorator pattern:
- There is no need to create subclasses to extend the functionality of an object.
- The ability to dynamically connect new functionality anywhere: before or after the main functionality of the ConcreteComponent object.
Proxy is a structural design pattern that provides an object that controls access to another object, intercepting and passing through all its calls.
The Proxy pattern provides a substitute object in place of the real object. This object controls access to the original object. Used very often.
Remember how we used the Mockito framework and intercepted a call to a real object using the Mockito.spy() method or the @Spy annotation? It was then that a special Proxy object was created, through which all calls to the original object passed.
And then we could manage these calls by adding rules to the object. That's right - the original object does not change, and working with it becomes much more flexible. It is especially useful when we do not call the proxy object from our code, but pass it somewhere. Thus controlling the communication of two objects independent of us.
Types of proxies by purpose:
- Logging proxy : logs all calls to the “Subject” with their parameters.
- Remote proxy (remote proxies): provides communication with the “Subject”, which is in a different address space or on a remote machine. It may also be responsible for encoding the request and its arguments and sending the encoded request to the real “Subject”.
- Virtual proxy (virtual proxies): ensures that the real "Subject" is created only when it is really needed. It can also cache some of the information about the real "Subject" to delay its creation.
- Copy-on-write : Provides a copy of the "subject" when the client performs certain actions (a special case of the "virtual proxy").
- Protection proxies : Can check if the caller has the necessary permissions to make the request.
- Caching Proxy : Provides temporary storage of calculation results before serving them to multiple clients who can share the results.
- Screening Proxy: Protects the "Subject" from dangerous clients (or vice versa).
- Synchronization Proxy : performs synchronized access control to the “Subject” in an asynchronous multi-threaded environment.
- “Smart” link (smart reference proxy): performs additional actions when a link to the “Subject” is created, for example, calculates the number of active links to the “Subject”.
The Bridge pattern is a structural design pattern used to "separate abstraction and implementation so that they can change independently."
The bridge pattern uses encapsulation, aggregation, and can use inheritance to share responsibility between classes.
When abstraction and implementation are separated, they can change independently. In other words, when implemented through the bridge pattern, changing the structure of the interface does not interfere with changing the structure of the implementation.
Consider such an abstraction as a figure. There are many types of shapes, each with its own properties and methods. However, there is something that unites all the figures. For example, each shape must be able to draw itself, scale, and so on.
At the same time, drawing graphics may differ depending on the type of OS or graphics library. Shapes should be able to draw themselves in various graphics environments. But it's impractical to implement all the drawing methods in each shape, or to modify the shape every time the drawing method changes.
In this case, the bridge pattern helps, allowing you to create new classes that will implement drawing in various graphical environments. Using this approach, it is very easy to add both new shapes and ways to draw them.
The connection represented by the arrow in the diagrams can have 2 meanings: a) “a kind”, in accordance with the Liskov substitution principle, and b) one of the possible implementations of the abstraction. Languages typically use inheritance to implement both a) and b), which tends to bloat class hierarchies.
The bridge serves exactly to solve this problem: objects are created in pairs from an object of a class of hierarchy A and hierarchy B, inheritance within the hierarchy A has the meaning of “variety” according to Liskov, and for the concept of “implementation of abstraction” a link from object A to its paired object B is used.
The Facade pattern is a structural design pattern that hides the complexity of a system by reducing all possible external calls to a single object that delegates them to the appropriate objects in the system.
How to provide a unified interface with a set of disparate implementations or interfaces, for example, to a subsystem, if strong coupling to that subsystem is undesirable, or the implementation of the subsystem might change?
Define one point of interaction with the subsystem - a facade object that provides a common interface with the subsystem, and assign it the responsibility for interacting with its components. A facade is an external object that provides a single entry point for subsystem services.
The implementation of other subsystem components is private and not visible to external components. Facade object provides an implementation of the GRASP pattern Resistant to changes in terms of protection against changes in the implementation of the subsystem.
Important! This pattern is used when we want to completely hide some group of objects and pass all communication with them through our object. If you just want to provide some control over the communication process of objects and not necessarily hide them, then it is better to use the Proxy pattern.