The Strategy pattern

Java's AWT provides implementations of common user interface components such as buttons, menus, scrollbars, and lists. Those components are laid out inside containers such as panels, dialog boxes, and windows. But AWT containers do not perform the actual layout; instead, those containers delegate layout functionality to another object known as a layout manager. That delegation is an example of the Strategy design pattern.

The Strategy design pattern encapsulates a family of algorithms, or strategies, by implementing each algorithm in a different class. Clients that employ those algorithms delegate functionality to objects instantiated from those classes. That encapsulation allows clients to vary algorithms by delegating to different objects.

For the AWT, the clients are containers and the family of algorithms are layout algorithms encapsulated in layout managers. If a particular layout algorithm other than the default algorithm is required for a specific container, an appropriate layout manager is instantiated and plugged into that container. This way, layout algorithms can vary independently from the containers that use them.

 

The Composite pattern

To facilitate complex user interface screens, user interface toolkits must allow nested containers, effectively composing components and containers into a tree structure. Additionally, it's crucial for components and containers in that tree structure to be treated uniformly, without having to distinguish between them. For example, when the AWT determines the preferred size of a complex layout containing nested components and containers, it walks the tree structure and asks each component and container for its preferred size. If that traversal of the tree structure required distinction between components and containers, it would unnecessarily complicate that code, making it harder to understand, modify, extend, and maintain.

The AWT accomplishes nesting containers and uniform treatment of components and containers by implementing the Composite pattern. The Composite pattern dictates that containers are components, typically with an abstract class that represents both. In the AWT, that abstract class is java.awt.Component, the superclass of java.awt.Container; therefore, an AWT container can be passed to the add(Component) method from java.awt.Container because containers are components.

 

The Decorator pattern

The java.io package provides a set of classes for reading input streams. Known as readers, each of those classes has a name that follows the pattern: xxxReader.

Readers provide specialized functionality. It's often necessary to combine the capabilities offered by java.io readers; for example, you might want to read from a file, keep track of line numbers, and push certain characters back on the input stream, all at the same time. The java.io package's designers could have used inheritance to provide a wide array of such reader combinations; for example, a FileReader class could have a LineNumberFileReader subclass, which could in turn have a PushBackLineNumberFileReader subclass. But using inheritance to compose the most widely used combinations of reader functionality would result in many classes.

The Decorator pattern is accomplished by enclosing an object in another object. The enclosing object forwards method calls to the enclosed object and typically adds some functionality of its own before or after forwarding. The enclosing object, decorator, conforms to the interface of the object it encloses, allowing the decorator to be used as though it were an instance of the object it encloses.