Structural ObjectOriented Design Patterns Albert Ritzhaupt Objectives Introduction
Structural Object-Oriented Design Patterns Albert Ritzhaupt
Objectives Introduction to Structural Patterns Adapter Pattern Bridge Pattern Composite Pattern Decorator Pattern Façade Pattern Flyweight Pattern Proxy Pattern Conclusion to Structural Patterns
Introduction to Structural Patterns are concerned with how classes and objects are composed to form larger structures Structural class patterns use inheritance to compose classes and implementations Structural objects describe ways to compose patterns to realize new functionality Structural patterns are related to some degree
Adapter Pattern Convert the interface of a class into another interface clients expect. Adapter lets classes work together that otherwise couldn’t because of incompatible interfaces
When to use the Adapter Pattern Need to use an existing class, and its interface does not match Create a reusable class that cooperates with unrelated or unforeseen classes that don’t necessarily have compatible interfaces Need to use several existing subclasses , but its impractical to adapt their interfaces by sub -classing every one. An object Adapter can adapt the interface of its parent class
Structure of the Adapter Client Target Adaptee Request() Specific. Request() Adaptor Specific. Request() Client Target Adaptee Request() Specific. Request() Adaptor Request() Adaptee. Specific. Request()
Structure of the Adapter Clients call operations on an Adapter instance In turn, the Adapter calls Adaptee operations that carry out the request
Consequences of the Adapter Adapts Adaptee to Target by committing to a concrete Adaptee class. A class adapter won’t work when we want to adapt a class and all its sub-classes Lets the Adaptor override the Adaptee’s behavior, since Adapter is a subclass of Adaptee Introduces only on object, and no additional pointer indirection is needed to get to the Adaptee
Consequences of the Adapter Lets a single Adaptor work with many Adaptees - that is, the Adaptee itself and all of its subclasses. The Adaptor can also add functionality to all Adaptees at once Makes it harder to override Adaptee behavior. It will require sub-classing Adaptee and making Adapter to refer to the subclass rather than the Adaptee itself.
Related to the Adapter Bridge has a similar structure to an Adapter, but they have different intents A Decorator enhances another object without changing its interface, thus it is more transparent than an Adapter The Proxy defines a representative or surrogate interface, but does not change its interface
Bridge Pattern Decouple an abstraction of an interface from its implementation so that the two can very independently
When to use the Bridge Pattern Need to avoid a permanent binding between an abstraction and its implementation. This might be the case when the implementation must be selected or switched at run-time Both the abstractions and their implementations should be extensible by sub-classing. The Bridge pattern can combine the different abstractions and implementations and extend them separately Changes in the implementation should have no impact on the client, meaning the code should not have to be recompiled
Structure of the Bridge Client Abstraction Implementor Operation() Operation. Imp() Imp. Operation. Imp() Redefined. Abstraction Concrete. Implementor. A Concrete. Implementor. B Operation. Imp()
Structure of the Bridge Abstraction forwards client requests to its implementor object
Consequences of the Bridge Decouples interface and implementation, thus an interface is not bound permanently to an interface Improves extensibility, since the abstraction and implementation can be extended independently Hides the implementation details from the client
Related to the Bridge An Abstract Factory can create and configure a particular Bridge The Adapter pattern is geared toward making unrelated classes work together, whereas is used up-front in a design to let abstractions and implementations very independently
Composite Pattern Compose classes into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly
When to use the Composite Pattern There is a need to represent part-whole hierarchies Need clients to be able to ignore the difference between compositions of objects and individual objects Clients will treat all objects in the composite structure uniformly
Structure of the Composite Component Client Operation() Add(Component) Remove(Component) Get. Child(int) Leaf Composite Operation() Add(Component) Remove(Component) Get. Child(int) For all g in children g. Operation()
Structure of the Composite Clients use the component interface to interact with objects in the composite structure If a recipient is a leaf, then the request is handled directly. If composite, then it usually forwards requests to its child components
Consequences of the Composite Defines class hierarchies consisting of primitive objects and composite objects Makes the client simple by treating composite and individual structure uniformly Makes it easier to add new kinds of components, clients don’t have to be changed for a new component class
Related to the Composite Often the component-parent is used for a Chain of Responsibility Decorator is often used with Composite using a common parent class Iterator can be used to traverse composites Visitor localizes operations that would normally be distributed across Composite and Leaf classes
Decorator Pattern Attach additional responsibilities to a client dynamically. Decorators provide a flexible alternative to sub classing for extending functionality
When to use the Decorator Pattern To add responsibilities to individual objects dynamically and transparently without affecting other objects When responsibilities can be withdrawn Extension by sub-classing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination A class definition may be hidden or otherwise unavailable for sub-classing
Structure of the Decorator Component Operation() Concrete. Component Decorator Operation() Component. Operation() Concrete. Decorator. A Concrete. Decorator Operation() Added. State Added. Behavior() Decorator. Operation() Added. Behavior()
Structure of the Decorator forwards requests to its component object It may optionally perform additional operations before and after forwarding the request
Consequences of the Decorator More flexibility than static inheritance, thus making it easier to add responsibilities to objects (even at run-time) Avoids feature-laden classes high up in the hierarchy, so instead of preparing for all foreseeable responsibilities, they can bee added on an as needed basis A Decorator and its components aren’t identical, thus you cannot rely on object identity Lead to lots of little objects that look alike, which can lead to the system being hard to learn and debug
Related to the Decorator A Decorator is different from an Adapter in that a Decorator only changes an object’s responsibilities A Decorator can be viewed as a degenerate Composite with only one component A Decorator lets you change the skin of an object; a Strategy lets you change the guts
Façade Pattern Provide a unified interface to a set of interfaces in a sub-system. Façade defines a higher-level interface that makes the sub-systems easier to use
When to use Façade Pattern To provide a simple interface to a complex subsystem. Sub-systems get often more complex as they evolve. A Façade can provide a simple default view of the sub-system that is good enough for most clients There are many dependencies between clients and the implementation classes of an abstraction. A Façade decouples the subsystem from clients, promoting subsystem independence and portability To layer subsystems by defining an entry point to each subsystem level
Structure of the Façade Sub-System
Structure of the Façade Clients communicate with a subsystem by sending requests to the Façade, which forwards them to the appropriate subsystem objects Although the subsystem objects perform the given work, the Façade may have work to do of its own to translate its interface to subsystem interfaces Clients that use the Façade don’t have to access its subsystem objects directly
Consequences of the Façade It shields the clients from subsystem components, thereby reducing the number of objects a client must deal with It promotes weak coupling between clients and subsystems, therefore components can very without affecting the clients It doesn’t prevent applications from using subsystem classes if they need to
Related to the Façade Abstract Factory can be used with a Façade to provide an interface for creating subsystem objects independently Mediator is similar to the in that it abstracts the functionality of existing classes The Façade’s are often implemented as Singletons
Flyweight Pattern Use sharing to support large numbers of fine-grained objects efficiently
When to use the Flyweight Pattern An application uses a large number of objects Storage costs are high because of their sheer quantity of objects Most object state can be made extrinsic Many groups of objects may be replaced by relatively few shared objects once extrinsic state is removed The application doesn’t depend on object identity
Structure of the Flyweight. Factory Flyweight Get. Flyweight(key) Operation(extrinsic. State) If(Flyweight(key). exists) Return existing Flyweight Else Create new Flyweight Add flyweight to pool Return new Flyweights End if Client Concrete. Flyweight Unshared. Concrete. Flyweight Operation(extrinsic. State) intrinsic. State
Structure of the Flyweight State that a Flyweight needs to function must be characterized as either intrinsic or extrinsic. Intrinsic is stored in the concrete flyweight object; extrinsic state is stored or computed by client objects. Clients pass this state to the flyweight when they invoke operations Clients shouldn’t instantiate concrete Flyweights directly. Clients should receive objects exclusively from the Flyweight factory object to ensure they are shared properly
Consequences of the Flyweights associate run-time costs associated with transferring, finding, and/or computing extrinsic state However, such costs are offset by space savings: reduction in the total number of instances that come from sharing; the amount of intrinsic state per object; whether extrinsic state is computer or stored There is a trade-off between the cost of intrinsic state and the extrinsic state for computation time
Related to the Flyweight The Flyweight is often combined with the Composite to implement a logical Hierarchy structure in terms of a directed-acyclic graph of with shared leaf nodes It’s often best to implement State and Strategy objects as Flyweights
Proxy Pattern Provide a surrogate or placeholder for another object to control access to it
When to use the Proxy Pattern A remote proxy provides a local representative object in a different address space A virtual proxy creates expensive objects on demand A protection proxy controls access to the original object A smart reference is a replacement for a bare pointer that performs additional actions when object is accessed
Structure of the Proxy Client Subject Request() Real. Subject Proxy Request() Real. Subject. Request()
Structure of the Proxy forwards requests to real subject when appropriate, depending on the type of Proxy There are three types of Proxies used in most implementations
Consequences of the Proxy A remote Proxy can hide the fact that an object resides in a different address space A virtual Proxy can perform optimizations such as creating an object on demand Both protection proxies and smart references allow additional housekeeping tasks when an object is accessed Copy-on-write allows the Proxy to prevent a large copy-computation unless necessary
Related to the Proxy An Adapter provides a different interface to the objects it adapts, however, the Proxy provides the same interface to its subjects and may refuse access to operations Although Decorators have a similar implementation, Decorators have a different purpose. A Decorator adds more responsibility to an object Proxies vary in the degree to which they may be implemented
Conclusion of Structural Patterns Structural patterns rely on the same small set of language mechanisms for structuring code and objects: single and multiple inheritance and object composition Structural pattern often rely on Creational patterns for instantiation
- Slides: 47