Structural patterns, as classified in the Gang of Four (GoF) design patterns, are powerful tools for programmers, primarily addressing the composition and arrangement of classes and objects within software systems. They confer several key benefits and rely on a range of fundamental object-oriented techniques.
Firstly, structural patterns facilitate better system design through the compartmentalization of responsibilities, fostering clean, efficient, and effective software architecture. They simplify complex relationships between different parts of the software, leading to less error-prone code. This inherently leads to enhanced software maintainability because well-structured code is easier to understand, debug, and update.
Secondly, structural patterns enhance code reusability. By defining ways to compose objects, they enable the same object structure to be used across different parts of a system or even in different systems. This leads to less redundant code, faster development times, and reduced likelihood of errors.
Thirdly, these patterns offer ways to manage the evolution of software systems. As system requirements change over time, structural patterns provide a blueprint for extension and modification, promoting system scalability and flexibility without necessitating wholesale redesigns.
Structural patterns draw upon several key object-oriented techniques to provide these benefits:
- Inheritance: Structural patterns extensively use inheritance to establish relationships and promote code reusability. For example, the Decorator pattern uses inheritance to extend the behavior of a base class.
- Encapsulation: Encapsulation is vital to hide implementation details and maintain the internal consistency of objects. The Facade pattern encapsulates a complex subsystem behind a simplified interface.
- Polymorphism: Structural patterns often leverage polymorphism to enable objects to take on many forms depending on the context. The Adapter pattern, for instance, relies on polymorphism to adapt one interface to another.
- Composition: Many structural patterns use composition to combine simple objects into complex ones, enhancing flexibility. The Composite pattern, for instance, treats single instances and compositions in the same way.
- Aggregation: This technique is used to represent relationships between different components. For example, the Proxy pattern uses aggregation to hold a reference to the object it controls.
Structural patterns are powerful tools for programmers, providing ways to simplify complex systems, promote reusability, and enable software evolution. They are built upon key object-oriented principles, thus aiding programmers in leveraging the full power of object-oriented programming.
Structural patterns include some quite sophisticated and powerful techniques. However, they are based on just a few basic techniques, primarily:
- Delegation: The pattern is one in which a given object provides an interface to a set of operations.
However, the actual work for those operations is performed by one or more other objects.
- Object composition: Other objects are stored as pointers or references inside the object that provides the interface to clients. Object composition is a powerful yet often overlooked tool in the OOP programmer's toolbox. Structural patterns show you how to take advantage of it.
In the context of design patterns, you will see classes that use other classes in their
construction. When one class passes off a task to another class, that is delegation. It’s
what makes composition so powerful.
With inheritance, each child IS-A part of another class or classes. With composition, objects may USE-A different class or group of classes for a series of tasks.
This does not mean that inheritance should not be used. In fact, most design patterns include inheritance and composition used together. Instead of using inheritance in a long series of child, grandchild, great-grandchild,
a design pattern approach encourages using shallow inheritance and using the functionality of more than a single class.
This approach helps to avoid tight binding and crashes occurring when changes are
made to designs where concrete classes have child classes.