The core idea behind the proxy design pattern is controlling access to objects. We define the placeholder or proxy object which represents another object or a remote service. Clients use this proxy, not the real object. The proxy forwards the client request to the actual subject as necessary.
- The proxy pattern acts as a surrogate for another object and controls access to it.
- The proxy may also perform optimizations, defer the instantiation of the underlying object, orprotect the resource from unauthorized access.
- We can use proxies in the following situations:
- Remote proxies allow access to remote resources like RESTful services. The remote proxy acts as a local placeholder for a remote object. Clients can call methods of this local representative, which in turn forwards the request to the remote object.
- Virtual proxies can be used to manage the creation of expensive resources and the execution of computationally intensive operations. We can improve performance and memory usage by postponing the costly operations until they are actually needed.
- Protective proxies control access to sensitive objects. The surrogate object checks whether the caller has the appropriate permissions required to use the protected resource.
The proxy pattern provides a placeholder for another object to control the way the underlying resource is accessed. The proxy introduces an additional level of interaction to support controlled, remote, or delayed access.
Example: Consider if you have to fetch and display data from network only when user does a particular action. Create a NetworkDataProxy class object set the URL and completion handler. This by itself should not download / fetch the remote content. Only when the particular user action is trigger the proxy object will fetch. This defers the downloading of content only on need basis.
Same can be done with an ImageProxy class which can be used to download image only on demand rather than pre-downloading before knowing the user needs.
- Allow access to remote resources like RESTful services. The remote proxy acts as a local placeholder for a remote object. Clients can call methods of this local representative which in turn forwards the request to the remote object.
- Manage the creation of expensive resources and the execution of computationally intensive operations.
- We can improve performance and memory usage by postponing the creation of expensive resources until they are first accessed.
- Control access to sensitive objects. The surrogate object checks whether the caller has the appropriate permissions required to use the protected resource.
You should not use the proxy pattern if none of these situations apply.
The proxy object should be used to perform all the required operations on the resource it represents. The benefits of the proxy pattern are rendered useless if clients can bypass the surrogate and use the underlying types directly.
Intent: The flyweight pattern is used to reduce the memory usage and the creation costs for similar objects that rely on the same data.
- Instead of creating multiple objects, the flyweight pattern shares one instance to represent multiple objects.
- Each flyweight object is divided into two pieces, the state-dependent extrinsic part, and the state-independent intrinsic part.
- The intrinsic state is stored in the flyweight object.
- To implement the flyweight pattern we must identify the immutable state free intrinsic set of properties from the state-dependent extrinsic state.
- The extrinsic part should be managed by clients and passed to the flyweight instance, whereas the immutable state of the flyweight should be protected from changes.
The flyweight pattern allows the sharing of common objects that can be used identically saving memory by reducing the number of object instances at runtime.
Example from sourcemaking.com:
The Flyweight uses sharing to support large numbers of objects efficiently. Modern web browsers use this technique to prevent loading same images twice. When browser loads a web page, it traverse through all images on that page. Browser loads all new images from Internet and places them the internal cache. For already loaded images, a flyweight object is created, which has some unique data like position within the page, but everything else is referenced to the cached one.
- The flyweight pattern should be used to support a large number of similar objects via sharing.
- Instead of creating multiple objects, the flyweight pattern shares one instance to represent multiple objects.
- To implement the flyweight pattern we must separate the immutable set of properties from the state-dependent part.
- The intrinsic part should be stored in the flyweight object whereas the state-dependent extrinsic part should be managed by clients. The extrinsic state is stored or computed by client objects and passed to the flyweight when its operations are revoked.
Common thing is clients can change the immutable state of the flyweight. The intrinsic state of the flyweight objects must not be modified by callers.
Another wrong implementation is not protecting the flyweight from concurrent access. A shared flyweight instance may be accessed in parallel from several different strats. Therefore, threat safety is imperative.
The facade is a very simple pattern intended to simplify the use of complicated APIs. It is presented as a single class with a straightforward interface which allows callers to execute common tasks. The Facade may wrap a type with a very complex interface or multiple components which need to be used together.
The Facade pattern simplifies the use of complex APIs by providing a higher-level, simplified interface.
- Using the simpler interface is just an option. Callers can still access the wrapped components for lower-level functionality.
- Complex interface is in fact, the consumer side too. The client code gets complicated and harder to maintain.
- Interface changes require refactoring on the caller side which can become a serious problem especially when relying on framework or software systems that are still under development or changed frequently.
- The Facade Pattern consolidates the functionality into a single class and hides the complexity by providing a simpler interface.
- Clients don’t have to use the complicated subsystem directly, instead, they communicate with the Facade.
- The Facade in turn, involves the APIs of the subsystem it simplifies.
- The Facade not only provides a simpler interface but it also decouples the client implementation from the complex API.
- If the subsystem interface changes, they only have to adapt the Facade class, not the client code.
A typical mobile app in today’s world needs a network layer which takes the responsibility of fetching or downloading the data. If that’s all what the network layer does then consumer of this layer has to take the responisibility of storing the data / files if success or retry if failed.
Using Facade Design Pattern we create a Downloader component which encapsulates the downloading and storing parts providing the user a sleek simple to use interface. The component can also internalise the retry parts and throw error only if unsuccessful on multiple attempts.
- The Facade Pattern wraps the types belonging to a complex subsystem and provides a unified higher level interface. Clients can use the type or the subsystem through a simpler API.
- Another benefit of the Facade Pattern is that it isolates the changes of the underlying type or subsystem from the callers so the client code must not be defactored when the wrapped type change.
- Use the Facade pattern to simplify the usage of complicated pulling designed interfaces or when adding a convenient layer makes sense.
- A correctly implemented Facade Pattern lets client use the common features of the wrapped subsystem without directly accessing the underlying types.
Disadvantage: Leaking the wrapped objects. Changes made to the underlying types will require modifications on the caller side too.
The Decorator Design Pattern allows adding new behavior to existing types. We can extend the functionality of the type without modifying its implementation which is a flexible alternative to subclassing.
The Decorator can be implement via wrapping the object to be enhanced. Extensions provide the ability to add additional methods to types without having to suppress or change their source code.
The Decorator Design Pattern adds new responsibilities to objects without modifying the code of the type used to create them
A Basic Color class lets you create color with RGB and HSB. What if I have to create color using hex values? Typically we subclass. Instead of subclassing we need to extend the same color class and add a function to create color with hex values.
Another example would be when ordering food online a typical burger or subway sandwich would cost x then user also has options to add extra cheese or steak. Sometimes the vendor provides option to add a drink to the cart. All this adding extras would cost additional charges. The final cost is caluclated with calling the decorators to add their cost to the cart over the base item.
Common misuses: Accidentally replacing the existing functionality of a type. Another misuse is that we may change the original purpose of a type by decorating it with unrelated behavior.
This pattern can be used to create recursive tree structures of related objects where any element can be a composition of objects or an individual object. Each element of the structure may be accessed in a uniform manner without having to know whether we use a collection or a leaf object.
- Clients can ignore the difference between leaf and composed objects, and only use one type or protocol when accessing the elements within the composite structure.
- All types participating in the composite must implement the same interface. So, here’s a formal definition.
- With the composite pattern, we can represent part, whole hierarchies of objects.
- We can access the elements without having to know whether we use a collection or a leaf object.
- Callers can rely on the same type or protocol to access the elements within the composite structure.
The composite pattern allows related individual objects and collections of objects to be treated uniformly.
Treat individula objects and collection of objects uniformly. Use the same interface to access the elements.
Here is an example from sourcemaking:
Although the example is abstract, arithmetic expressions are Composites. An arithmetic expression consists of an operand, an operator (+ – * /), and another operand. The operand can be a number, or another arithmetic expression. Thus, 2 + 3 and (2 + 3) + (4 * 6) are both valid expressions.
One more example is of FileManager directory listing. An entry in file system can be a directory or a file. A directory can contain both directories and files. Here we make an file system entry interface and make directory and file class implement. Directory will composite an array of file system entry which can either be dir or a file.
Summary: Leaf and composite objects must implement the same interface so that they can be treated uniformly. With the composite pattern, we can represent part, whole hierarchies of objects. We can access the elements without having to know whether we use a collection or a leaf object. Callers can rely on the same type or protocol to access the elements within the composite structure.
Common failures: one of the problems you may face when implementing the composite pattern is that your design may become overly general.
Why? Increasing feature set explode class hierarchies. The Bridge pattern dissolves complex class hierarchies by separating common and specific functionality into different hierarchies.
In a typical software development as we add new features, the number of subclasses increases at an alarming rate. By decoupling an implementation from an interface, both can vary independently.
Goal is to separate out common functionality and specific ones.
The bridge pattern separates the abstraction from its implementation and reduces the impact of the changes.
Consider 2 classes – GroupChat & DirectChat. Now if these were in plain chat format. Then you get a requirement to add support for Secure Chat and Self-destructing chat, now general tendency is to extend the each chat into 2 others SecureGroupChat and SelfDestructGroupChat also SecureDirectChat and SelfDestructDirectChat. Now consider if there is another type of chat – BroadcastChat and we need to extend this to SecureBroadcastChat and SelfDestructBroadcastChat. Now we have a total of 9 classes.
Brdge pattern – solves this with interfaces and a bridge between the interface.
Using bridge pattern all Chat classes implement ChatInterface which will include a MessageInterface. On the other side all 3 Secure, SelfDestruct and Plain Message types will conform to MessageInterface.
- Separates Common and specific functionality
- Increases Cohesiveness
- Removes inheritence (hierarchies)
- Removes tight coupling
- Reduces number of classes
Motivation: When we need to adapt a framework but we cannot or do not want to modify its interface, yet we want to use it. The aim of the Adapter is to take the incompatible interface and adapt it to the one we need.
- The Adapter should be used to make things work after they are designed.
- Adapter is a right fit only when functionality is available but not in right interface.
- Choose the adapter if you cannot change the source code of the incompatible type.
- The Adapter is what actually adapts the incompatible interface to the one we require.
- Don’t use an Adapter if you can modify the implementation.
- You can also wrap the Adapter object in a dedicated Adapter class which exposes the API we need.
The Adapter pattern converts an existinginterface to another interface, which is required to make it compatible with the rest of the software system.
This pattern is useful when we need to integrate a component that provides similar functionality to other components in the system, but comes with an incompatible interface.
Example: What do we do when we have a HDMI port on our laptop and we need to connect it to a monitor with VGA? We use an adapter or a converter.
Let’s say you need to build an Photo sharing app which edits a photo and shares it on all social media – but interface for each one is different with various parameters. Facebook has its own parameter list, Twitter has different set, Tumblr has a different set. So we write a common (adapter) interface instead of using different interface for each of them. This keeps your app code clean and readable.
Summary: The Adapter pattern converts an incompatible interface into one that we need. The adapter can save us from a lot of refactoring work when integrating incompatible interfaces. By using the Adapter, we do not have to refactor our code to use the adaptee’s (framework’s) incompatible interface.
A common mistake we do is trying to adapt a component which does not provide the required functionality.