Structural Design Patterns


Structural Design Patterns are Design Patterns that help the design by identifying a simple way to realize relationships between entities. The Creational Patterns help us generate objects. But, they leave an open end in associating the objects and identifying the relationships with each other. The structural patterns help us with just that.

Adapter


As the name suggests, the Adapter pattern allows us to adapt the available interface to what we need. A voltage adapter helps us obtain 110V out of a 230V socket. Similarly, the Adapter pattern provides for an adapter class that can map the available interface to the required interface.

This pattern has three main roles - client, adapter and the adaptee.

The thin Adapter is one that just maps the method names. For example, if we have an adaptee that provides a method trigger(), and the client needs the same functionality in a method name start(), the adapter provides a method named start() that simply invokes the method trigger(). This is a thin adapter. A thick adapter could do a lot more than that.

Here, we must be careful to make sure that the adapter is only adapting the interface - and not adding a functionality of its own. This separation of concern is very important to ensure maintainability.

Bridge Pattern


OOAD is all about separating the abstraction from implementation. The Bridge Pattern takes this a step forward.

It encapsulates an implementation class inside of an interface class. The bridge pattern allows the Abstraction and the Implementation to be developed independently. The client code needs to access only the Abstraction - without being concerned about the Implementation. The abstraction is an interface or abstract class and the implementor is also an interface or abstract class.

The abstraction contains a reference to the implementor. Children of the abstraction are referred to as refined abstractions, and children of the implementor are concrete implementors. Since we can change the reference to the implementor in the abstraction, we are able to change the abstraction's implementor at run-time. Changes to the implementor do not affect client code.

The Bridge Pattern has four participants:

  • Abstraction: The core of the bridge design pattern and defines the crux. Contains a reference to the implementer.
  • Refined Abstraction: It extends the abstraction takes the finer detail one level below. Hides the finer elements from implemetors.
  • Implementer: It defines the interface for implementation classes. This interface does not need to correspond directly to the abstraction interface and can be very different. Abstraction imp provides an implementation in terms of operations provided by Implementer interface.
  • Concrete Implementation: Implements the above implementer by providing concrete implementation.

Composite Pattern


When we think of object oriented programming, we think of inheritance. But inheritance is not always the best way of attacking a problem. Inheritance should be used only when the a class inherits from the other in spirit - not because it has similar functionality.

For example, a circle is a shape. But, an idol has a shape. In its spirit, an idol has a lot more than a shape. A circle too is more than just a shape. But, in its spirit, it is just a shape. In plain English, a circle "Is a" shape, but an idol "Has a" shape. The inheritance is called an "Is a" relation while composition is "Has a" relation.

The composition pattern provides a formal definition for this composition. Here, the container class contains a reference to an interface in the hierarchy of implementing objects.

The composition pattern has four different players

  • Component - Component declares the interface for objects in the composition and for accessing and managing its child components. It also implements default behavior for the interface common to all classes as appropriate.
  • Leaf - Leaf defines behavior for primitive objects in the composition. It represents leaf objects in the composition.
  • Composite - Composite stores child components and implements child related operations in the component interface.
  • Client - Client manipulates the objects in the composition through the component interface.

The client uses the component class interface to interact with objects in the composition structure. If recipient is a leaf then request is handled directly. If recipient is a composite, then it usually forwards request to its child components, possibly performing additional operations before and after forwarding.

Decorator Pattern


As the name suggests, a Decorator "decorates" a class. The code is pretty similar to a container class. But the concept is different. A decorator is used when we want to add a lot more functionality to the given class - that does not make a case for inheritance.

Note that inheritance is used only when we have a genuine "Is a" relationship. When we just want to add more functionality, we should use a decorator. This essentially contains the original class object and adds more functionality to it. The calls are passed over to the contained class - perhaps with some more logic before and after it. The decorator can also have its own methods.

To elaborating again, that is exactly what we do with inheritance. But we should use decoration rather than inheritance when the extension does not merge well in the class hierarchy. For example, consider the hierarchy - Shape - Polygon - Quadrilateral - Rectangle - Square.

Now if we have a "Yellow Rectangle", it does not fit well in this hierarchy. The language does not prevent us from using inheritance here. But, the design principles do. Instead, here we should use a decorator class - Yellow Shape - that can have a hierarchy of its own. The Yellow Rectangle would contain an object of class Rectangle.

The Decorator pattern has four participants:

  • Component - Defines the interface for objects that can have responsibilities added to them dynamically.
  • ConcreteComponent - Defines an object to which additional responsibilities can be attached.
  • Decorator - Maintains a reference to a Component object and defines an interface that conforms to Component's interface.
  • ConcreteDecorator - Adds responsibilities to the component.

Façade Pattern


A real life application is never implemented as a monolithic block. That is a sure way to disaster. The application should be divided into multiple subsystems that interact with each other according to a defined protocol. This makes things more manageable and extensible - so that a change in one component does not impact the others. If we can achieve such isolation, we can guess that we are on the right track.

Just having multiple subsystems is not enough. The subsystems should be separate in spirit - not just in code - else an isolation is impossible. If the subsystems are truly independent, then the implementation of one has no relation with that of the other - so long as they follow the agreed interface.

In object oriented design, such a protocol is often implemented in form of a façade class. The façade class receives method calls from the rest of the application and passes them to the appropriate code within the subsystem - that is isolated from the application.

There is an important point we must remember when working with a façade class. Ideally the façade class should just pass on requests to the appropriate code within the module. But, at times developers tend to use it as a "shock absorber" - that just absorbs any changes that are required. This bloats the façade class and then damages the design pattern - reducing the maintainability. The façade class should remain a façade and nothing more than that.

Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use. For example, consider a compiler that has many components hidden behind a simple interface.

There are two participants in this pattern:

  • Façade - Knows which subsystem classes are responsible for a request. Delegates client requests to appropriate subsystem objects.
  • Subsystem classes - Implement subsystem functionality. Handle work assigned by the Façade object. Have no knowledge of the façade; that is, they keep no references to it.

Flyweight Pattern


We often encounter a situation where we need to create too many instances of a given class. If such an object is complex , and its creation requires a lot of computation, this can create performance issues and also risk an out of memory exception. The flyweight pattern tries to avoid this scenario.

The flyweight pattern tries to reduce the number of objects by "reusing" a single instance of the class. That is, it creates a single instance of each type of class and passes a reference to any client that needs to use it.

But this is not so easy to implement. For it to work properly, we need to ensure a few aspects of the code. The foremost requirement is that the objects of the given class should be immutable - that is, the internal state of the object should not change once the object is created.

This is required, because if we have multiple clients using it simultaneously, we do not want to land in a situation where two clients are trying to update it simultaneously. This can get even more complex if we have multi-threading. But even in a single thread operation, we can have the client code using two references to the same object - where updates to one will impact the other invocation.

One might say what is the point if we do not allow the object to change its state. We want the object to track a state as it works. We can't have all the invocations to return the same result!

That is true. In order to use the flyweight pattern, we need to split the class into intrinsic state and extrinsic state. The intrinsic state is the immutable state of the object that is tracked within the object. The extrinsic state is specific to the client invocation and is tracked by the client. Each method invocation takes this extrinsic state as an input parameter and works accordingly. The method invocation alters the extrinsic state and not the intrinsic state. Hence the object remains immutable.

That leads to another question - what is the meaning in having an extrinsic state? If we separate the method and the state, we are violating the fundamental principle of object oriented programming. That is right. If the extrinsic state is a major component of the object state, there is no point in using the flyweight pattern. But if the intrinsic state is much more intricate and heavier than the extrinsic state, it makes sense to keep the extrinsic state away from the object.

The flyweight pattern has the following roles:

  • Instance objects: The individual objects of the required intrinsic state.
  • Factory: The class that takes care of instantiating the required objects. Typically it owns a map with keys to identify the individual objects that are the values. It instantiates the particular object with the required intrinsic configuration and puts it into the map - only when required. It may have a few instances of the same class - for different intrinsic configurations. But, one must be careful if we end up with too many of these instances, we will be violating the principle of flyweight pattern. Of course, the factory should export methods to get a reference of the individual objects using an interface.
  • Client: The client is the code that uses these objects. It invokes methods on the factory object to get a reference to the configured object. Then it invokes individual methods in the configured object, along with the required extrinsic state that it maintains - to achieve the required functionality.

Proxy Pattern


Most of us have used a proxy server to access the internet. An HTTP proxy server is essentially a server on our campus, that controls all the HTTP requests made to the outside world. An HTTP request that I make from my PC bangs on the proxy server, which verifies its sanity and then makes the call on my behalf. It then verifies the sanity of the HTTP response and returns it back to me. It gives me a feeling that I am connected to the internet. But in reality, I am only connected to the proxy server.

The proxy design pattern does just that. The proxy object provides a kind of transparent wall between the caller and the callee. In the code, this is pretty similar to the adapter or decorator patterns. But the design concept is very different. The adapter is used to alter an interface and the decorator is used to add value. But the proxy pattern does neither of these. It is just transparent.

Then what does it do? What is the great deal we achieve by implementing a proxy? A proxy class can be used in one or more of the following scenarios:

  • A virtual proxy is a placeholder for "expensive to create" objects. The real object is only created when a client first requests/accesses the object. Until then, the proxy server handles the show.
  • A remote proxy provides a local representative for an object that resides in a different address space. This provides a single gateway for the external communications. It can also help reduce unnecessary remote invocations by responding to requests that can be locally available.
  • A protective proxy controls access to a sensitive master object. The "surrogate" object checks that the caller has the access permissions required prior to forwarding the request.
  • A smart proxy interposes additional actions when an object is accessed. Typical uses include memory management (ensure objects are freed when not required), thread management(ensure lock on the sensitive object)

The Proxy pattern has the following roles:

  • Client: The object that invokes a method
  • Subject: An interface that defines the method to be invoked on the proxy and the real subject
  • Proxy: The concrete class that implements the Subject interface and takes care of passing the invocation to the real subject
  • Real Subject: The real code that hides behind the proxy.