Structural design patterns deal with the relationship between objects and how this can be simplified. It encourages you to create classes that accept interfaces rather than concrete classes so your code will be loosely coupled.
Problem - you need to get two incompatible interfaces to work together. You need to adapt a target class so that it has the same interface that a client expects. Any class can work together as long as the adapter solves the issue that all classes must implement every method defined by the shared interface
Solution - the adapter pattern defines a separate adapter class that converts the incompatable interface of an adaptee class into another interface the target client requires. The adapter class adapts the interface of an already existing class without changing it. The adapter class wraps the object to hide the complexity of the conversion happening behind the scenes.
Problem - you have a monolithic class that has several variants of some functionality that you want to divide and organise. Say you have a shape class with two circle and square subclasses and you want to extend the classes to incorporate red and blue so you end up with four subclasses: RedCircle, BlueCircle, Redsquare and BlueSquare. Adding new shapes and colours would make the hierarchy grow exponentially.
Solution - the bridge pattern lets you separate a large class or a set of closely related subclasses into two separate hierarchies - abstraction and implementation - which can be developed independently of each other. Instead of having all state and behaviours in one class, different dimensions (eg form and colour) are extracted into a separate class hierarchy. Colour related code can be extracted into subclasses (eg red and blue) and the shape class gets a reference field pointing to one of the color objects and this reference field acts as a bridge between colour and shape. The bridge pattern lets you split the monolithic class into several class hierarchies. After this, you can change the classes in each hierarchy independently of the classes in the others. This approach simplifies code maintenance and minimizes the risk of breaking existing code.
Problem - you have to implement a tree like object structure. Imagine you have two types of objects - products and boxes. The boxes can contain products as well as smaller boxes. These smaller boxes can contain products and even smaller boxes. If you had to calculate the cost of all items, iterating is difficult as each box object has nested levels.
Solution - the composite pattern suggests you work with products and boxes through a common interface. If you had to calculate total price of all products in all boxes, the compound shape passes the request recursively to all its children and sums up the result. The client code works with all objects through a single interface so it will not know if it is working with a simple or compound object. The client is not coupled to concrete classes.
Problem - you want to add new functionality to an existing object without altering its structure.
Solution - you create a decorator class which wraps the original class. An interface is created and then concrete classes that implement this interface. You then create an abstract decorator class that also implements the interface and takes the concrete class in its constructor. A decorator can wrap other decorators.
Problem - you have complicated code that you want hidden behind a front-facing interface.
Solution - the facade class is a wrapper class. To make a complex subsystem easier to use, the facade object provides a simple interface that masks the complicated interface of a subsystem. It improves readability and reuseability.
Problem - you need to create a large number of similar objects. You can reduce memory usage by sharing objects that are similar in some way rather than creating new ones.
Solution - the constant data of an object is usually called the intrinsic state. It lives within the object; other objects can only read it, not change it. The rest of the object’s state, often altered from the outside by other objects is called the extrinsic state. The flyweight pattern suggests that you stop storing the extrinsic state inside the object. Instead, you should pass this state to specific methods which rely on it. Only the intrinsic state stays within the object, letting you reuse it in different contexts. Since the flyweight object (object containing only intrinsic state) can be used in different contexts, you have to make sure that its state can’t be modified. A flyweight should initialize its state just once, via constructor parameters. You can create a factory method that manages a pool of flyweight objects.
Problem - you want to control access to an object and additional functionality should be provided when accessing an object. For example, when accessing sensitive objects, it checks that the client has the needed access rights.
Solution - a proxy is a wrapper object that is being called by the client to access the real serving object behind the scenes. The proxy can simply forward to the real object or can provide additional logic. For the client, usage of a proxy object is similar to using the real object because both implement the same interface.