Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

UNIT-3 Structural Patterns: Intent

Download as pdf or txt
Download as pdf or txt
You are on page 1of 16

UNIT-3

Structural Patterns

Introduction:
Structural patterns are concerned with how classes and objects are composed to form
larger structures.
Structural class patterns use inheritance to compose interfaces or implementations.
Example: The class form of the Adapter pattern.
In general, an adapter makes one interface (the adaptee's) conform to another,
thereby providing a uniform abstraction of different interfaces.
A class adapter accomplishes this by inheriting privately from an adaptee class.
The adapter then expresses its interface in terms of the adaptee's.
Rather than composing interfaces or implementations, structural object patterns
describe ways to compose objects to realize new functionality.
The object composition becomes flexible due to the ability to change the composition at
run-time, which is impossible with static class composition.
Example: Composite pattern.
How to build a class hierarchy made up of classes for two kinds of objects:
primitive and composite.
The composite objects let you compose primitive and other composite objects into
arbitrarily complex structures.
3.1Adapter:
Intent:
Convert the interface of a class into another interface clients expect. Adapter lets classes
work together that couldn't otherwise because of incompatible interfaces.
Also Known As: Wrapper
Motivation:
Sometimes a toolkit class that's designed for reuse isn't reusable only because its
interface doesn't match the domain-specific interface an application requires.
Consider for example a drawing editor that lets users draw and arrange graphical
elements (lines, polygons, text, etc.) into pictures and diagrams.

P. Suresh, Asst. Prof., Dept. of CSE, CREC Page 76


The interface for graphical objects is defined by an abstract class called Shape.
The editor defines a subclass of Shape for each kind of graphical object.
Classes for elementary geometric shapes are easy to implement. But a TextShape
subclass that can display and edit text is more difficult to implement. (screen updates,
buffer updates)
How can existing and unrelated classes like TextView work in an application that
expects classes with a different and incompatible interface?
We could define TextShape so that it adapts the TextView interface to Shape's.
(1) by inheriting Shape's interface and TextView's implementation or
(2) by composing a TextView instance within a TextShape and implementing
TextShape in terms of TextView's interface.

Applicability:
Use the Adapter pattern when
you want to use an existing class, and its interface does not match the one you need.
you want to create a reusable class that cooperates with unrelated or unforeseen classes,
that is, classes that don't necessarily have compatible interfaces.
(object adapter only) you need to use several existing subclasses, but it's impractical to
adapt their interface by subclassing every one. An object adapter can adapt the
interface of its parent class.

P. Suresh, Asst. Prof., Dept. of CSE, CREC Page 77


Structure:
Class adapter

Object adapter

Participants:
Target (Shape)
defines the domain-specific interface that Client uses.
Client (DrawingEditor)
collaborates with objects conforming to the Target interface.
Adaptee (TextView)
defines an existing interface that needs adapting.
Adapter (TextShape)

P. Suresh, Asst. Prof., Dept. of CSE, CREC Page 78


adapts the interface of Adaptee to the Target interface.
Collaborations:
Clients call operations on an Adapter instance. In turn, the adapter calls Adaptee
operations that carry out the request.
Consequences:
Class and object adapters have different trade-offs. A class adapter
adapts Adaptee to Target by committing to a concrete Adapter class.
lets Adapter override some of Adaptee's behavior, since Adapter is a subclass of
Adaptee.
introduces only one object, and no additional pointer indirection is needed to get to
the adaptee.
An object adapter
lets a single Adapter work with many Adaptees
makes it harder to override Adaptee behavior.
Here are other issues to consider when using the Adapter pattern:
How much adapting does Adapter do?
Pluggable adapters: describe classes with built-in interface adaptation.
Using two-way adapters to provide transparency.

Implementation:
Here are some issues to keep in mind while implementing Adapter:
1. Implementing class adapters in C++. Adapter would inherit publicly from Target and
privately from Adaptee.
2. Pluggable adapters.

P. Suresh, Asst. Prof., Dept. of CSE, CREC Page 79


find a "narrow" interface for Adaptee
The narrow interface leads to three implementation approaches:
a. Using abstract operations.
b. Using delegate objects.
c. Parameterized adapters.
Sample Code:
class Shape {
public:
Shape();
virtual void BoundingBox(Point& bottomLeft, Point& topRight) const;
virtual Manipulator* CreateManipulator() const;};
class TextView {
public:
TextView();
voidGetOrigin(Coord& x, Coord& y) const;
voidGetExtent(Coord& width, Coord& height) const;
virtual bool IsEmpty() const;
};
class TextShape : public Shape, private TextView {
public:
TextShape();
virtual void BoundingBox(Point& bottomLeft, Point& topRight) const;
virtual bool IsEmpty() const;
virtual Manipulator* CreateManipulator() const;};
Known Uses:
This pattern is used in the following toolkits: ET++Draw, InterViews 2.6,
ObjectWorks\Smalltalk, NeXT'sAppKit
Related Patterns:
Bridge: has same structure but different intent.
Decorator: enhances another object without changing its interface.
Proxy: representative or surrogate for another object and does not change its interface.
Factory Method is also a related pattern.

P. Suresh, Asst. Prof., Dept. of CSE, CREC Page 80


3.2 Bridge:
Intent:
Decouple an abstraction from its implementation so that the two can vary independently.
Also Known As: Handle/Body
Motivation:
When an abstraction can have one of several possible implementations, the usual way
to accommodate them is to use inheritance.
An abstract class defines the interface to the abstraction, and concrete subclasses
implement it in different ways.
But this approach isn't always flexible enough.

But this approach has two drawbacks:


It's inconvenient to extend the Window abstraction to cover different kinds of
windows or new platforms.
It makes client code platform-dependent.

P. Suresh, Asst. Prof., Dept. of CSE, CREC Page 81


Applicability:
Use the Bridge pattern when
you want to avoid a permanent binding between an abstraction and its
implementation.
both the abstractions and their implementations should be extensible by
subclassing.
changes in the implementation of an abstraction should have no impact on clients;
(C++) you want to hide the implementation of an abstraction completely from
clients.
you have a proliferation of classes as shown earlier in the first Motivation diagram.
you want to share an implementation among multiple objects, and this fact should
be hidden from the client.

P. Suresh, Asst. Prof., Dept. of CSE, CREC Page 82


Structure:

Participants:
Abstraction (Window)
defines the abstraction's interface.
maintains a reference to an object of type Implementor.
RefinedAbstraction (IconWindow)
Extends the interface defined by Abstraction.
Implementor (WindowImp)
defines the interface for implementation classes. Typically the Implementor
interface provides only primitive operations, and Abstraction defines higher-
level operations based on these primitives.
ConcreteImplementor (XWindowImp, PMWindowImp)
implements the Implementor interface and defines its concrete implementation.
Collaborations:
Abstraction forwards client requests to its Implementor object.

Consequences:
The Bridge pattern has the following consequences:
1. Decoupling interface and implementation.
Decoupling Abstraction and Implementor also eliminates compile-time
dependencies on the implementation.

P. Suresh, Asst. Prof., Dept. of CSE, CREC Page 83


2. Improved extensibility.
You can extend the Abstraction and Implementor hierarchies independently.
3. Hiding implementation details from clients.
You can shield clients from implementation details, like the sharing of
implementor objects and the accompanying reference count mechanism (if any).

Implementation:
Consider the following implementation issues when applying the Bridge pattern:
1. Only one Implementor.
Useful when a change in the implementation of a class must not affect its existing
clients
2. Creating the right Implementor object.
How, when, and where do you decide which Implementor class to instantiate
when there's more than one?
3. Sharing implementors.
4. Using multiple inheritance.

Sample Code:
class Window {
public:
Window(View* contents);
// requests handled by window
virtual void DrawContents();
virtual void Open();
virtual void Close();
virtual void Iconify();
virtual void Deiconify();
// requests forwarded to implementation
virtual void SetOrigin(const Point& at);
virtual void SetExtent(const Point& extent);
virtual void Raise();
virtual void Lower();

P. Suresh, Asst. Prof., Dept. of CSE, CREC Page 84


virtual void DrawLine(const Point&, const Point&);
virtual void DrawRect(const Point&, const Point&);
virtual void DrawPolygon(const Point[], int n);
virtual void DrawText(const char*, const Point&);
protected:
WindowImp* GetWindowImp();
View* GetView();
private:
WindowImp* _imp;
View* _contents; // the window's contents
};
Known Uses:
In ET++, WindowImp is called "WindowPort" and has subclasses such as XWindowPort
and SunWindowPort.
The ET++ Window/WindowPort design extends the Bridge pattern in that the
WindowPort also keeps a reference back to the Window.
libg++ defines classes that implement common data structures, such as Set, LinkedSet,
HashSet, LinkedList, and HashTable.
NeXT's AppKit uses the Bridge pattern in the implementation and display of graphical
images.
Related Patterns:
An Abstract Factory can create and configure a particular Bridge.
The Adapter pattern is geared toward making unrelated classes work together.

3.3 Composite:
Intent:
Compose objects into tree structures to represent part-whole hierarchies. Composite lets
clients treat individual objects and compositions of objects uniformly.
Motivation:
Graphics applications like drawing editors and schematic capture systems let users
build complex diagrams out of simple components.

P. Suresh, Asst. Prof., Dept. of CSE, CREC Page 85


Problem:
Code that uses these classes must treat primitive and container objects differently, even if
most of the time the user treats them identically. Having to distinguish these objects makes the
application more complex.
Solution:
The Composite pattern describes how to use recursive composition so that clients don't have to
make this distinction.
The key to the Composite pattern is an abstract class (Graphic) that represents both
primitives and their containers.
Graphic declares operations like Draw that are specific to graphical objects.
It also declares operations that all composite objects share, such as operations for
accessing and managing its children.
The subclasses Line, Rectangle, and Text define primitive graphical objects.
Since primitive graphics have no child graphics, none of these subclasses implements
child-related operations.
The Picture class defines an aggregate of Graphic objects.
Because the Picture interface conforms to the Graphic interface, Picture objects can
compose other Pictures recursively.

P. Suresh, Asst. Prof., Dept. of CSE, CREC Page 86


The following diagram shows a typical composite object structure of recursively
composed Graphic objects:

Applicability:
Use the Composite pattern when
you want to represent part-whole hierarchies of objects.
you want 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:

P. Suresh, Asst. Prof., Dept. of CSE, CREC Page 87


A typical Composite object structure might look like this:

Participants:
Component (Graphic)
declares the interface for objects in the composition.
implements default behavior for the interface common to all classes, as
appropriate.
declares an interface for accessing and managing its child components.
(optional) defines an interface for accessing a component's parent in the
recursive structure, and implements it if that's appropriate.
Leaf (Rectangle, Line, Text, etc.)
represents leaf objects in the composition. A leaf has no children.
defines behavior for primitive objects in the composition.
Composite (Picture)
defines behavior for components having children.
stores child components.
implements child-related operations in the Component interface.
Client
manipulates objects in the composition through the Component interface.
Collaborations:
Clients use the Component class interface to interact with objects in the
composite structure. If the recipient is a Leaf, then the request is handled
directly. If the recipient is a Composite, then it usually forwards requests to its
child components, possibly performing additional operations before and/or after
forwarding.

P. Suresh, Asst. Prof., Dept. of CSE, CREC Page 88


Consequences:
The Composite pattern
defines class hierarchies consisting of primitive objects and composite objects.
makes the client simple. Clients can treat composite structures and individual objects
uniformly.
makes it easier to add new kinds of components. Clients don't have to be changed for
new Component classes.
can make your design overly general. The disadvantage of making it easy to add new
components is that it makes it harder to restrict the components of a composite.
You'll have to use run-time checks instead.

Implementation:
There are many issues to consider when implementing the Composite pattern:
Explicit parent references.
Sharing components.
Maximizing the Component interface.
Declaring the child management operations.
The decision involves a trade-off between safety and transparency:
Defining the child management interface at the root of the class hierarchy gives
you transparency.
Defining child management in the Composite class gives you safety.
Should Component implement a list of Components?
Child ordering.
Caching to improve performance.
Who should delete components?
What's the best data structure for storing components? The choice of data structure
depends on efficiency.

Sample Code:
class Equipment {
public:
virtual ~Equipment();

P. Suresh, Asst. Prof., Dept. of CSE, CREC Page 89


const char* Name(){ return _name; }
virtual Watt Power();
virtual Currency NetPrice();
virtual Currency DiscountPrice();
virtual void Add(Equipment*);
virtual void Remove(Equipment*);
virtual Iterator* CreateIterator();
protected:
Equipment(const char*);
private:
const char* _name;
};

class FloppyDisk: public Equipment {


public:
FloppyDisk(const char*);
virtual ~FloppyDisk();
virtual Watt Power();
virtual Currency NetPrice();
virtual Currency DiscountPrice();
};

Currency CompositeEquipment::NetPrice () {
Iterator* i = CreateIterator();
Currency total = 0;
for (i->First(); !i->IsDone(); i->Next()) {
total += i->CurrentItem()->NetPrice();
}
delete i;
return total;
}

P. Suresh, Asst. Prof., Dept. of CSE, CREC Page 90


class CompositeEquipment : public Equipment {
public:
virtual ~CompositeEquipment();
virtual Watt Power();
virtual Currency NetPrice();
virtual Currency DiscountPrice();
virtual void Add(Equipment*);
virtual void Remove(Equipment*);
virtual Iterator* CreateIterator();
protected:
CompositeEquipment(const char*);
private:
List _equipment;
};

Known Uses:
The original View class of Smalltalk Model/View/Controller was a Composite, and
nearly every user interface toolkit or framework has followed in its steps, including
ET++ and InterViews, Graphics, and Glyphs.
The RTL Smalltalk compiler framework uses the Composite pattern extensively.
Another example of this pattern occurs in the financial domain, where a portfolio
aggregates individual assets.
The Command pattern describes how Command objects can be composed and
sequenced with a MacroCommand Composite class.
Related Patterns:
Often the component-parent link is used for a Chain of Responsibility.
Decorator is often used with Composite.
Flyweight lets you share components, but they can no longer refer to their parents.
Iterator can be used to traverse composites.
Visitor localizes operations and behavior that would otherwise be distributed across
Composite and Leaf classes.

P. Suresh, Asst. Prof., Dept. of CSE, CREC Page 91

You might also like