3. 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. The drawing editor's key abstraction is the graphical
object, which has an editable shape and can draw itself. 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: a
LineShape class for lines, a PolygonShape class for polygons, and so forth.
Classes for elementary geometric shapes like LineShape and PolygonShape are rather easy to implement,
because their drawing and editing capabilities are inherently limited. But a TextShape subclass that can
display and edit text is considerably more difficult to implement, since even basic text editing involves
complicated screen update and buffer management. Meanwhile, an off-the-shelf user interface toolkit
might already provide a sophisticated TextView class for displaying and editing text. Ideally we'd like to
reuse TextView to implement TextShape, but the toolkit wasn't designed with Shape classes in mind. So
we can't use TextView and Shape objects interchangeably.
we could define TextShape so that it adapts the TextView interface to Shape's. We can do this in one of
two ways: (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.
These two approaches correspond to the class and object versions of the Adapter pattern. We call
TextShape an adapter.
5. 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.
9. 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)
adapts the interface of Adaptee to the Target interface.
11. Consequences
Class and object adapters have different trade-offs. A class adapter
adapts Adaptee to Target by committing to a concrete Adapter class. As a
consequence, a class adapter won't work when we want to adapt a class
and all its subclasses.
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—that is, the Adaptee itself
and all of its subclasses (if any). The Adapter can also add functionality to
all Adaptees at once.
makes it harder to override Adaptee behavior. It will require subclassing
Adaptee and making Adapter refer to the subclass rather than the Adaptee
itself.
12. Consequences
How much adapting does Adapter do? Adapters vary in the amount of work they do to
adapt Adaptee to the Target interface. There is a spectrum of possible work, from simple
interface conversion—for example, changing the names of operations—to supporting an
entirely different set of operations.
Pluggable adapters. A class is more reusable when you minimize the assumptions other
classes must make to use it. By building interface adaptation into a class, you eliminate
the assumption that other classes see the same interface.
Using two-way adapters to provide transparency. A potential problem with adapters is
that they aren't transparent to all clients. An adapted object no longer conforms to the
Adaptee interface, so it can't be used as is wherever an Adaptee object can. Two-way
adapters can provide such transparency. Specifically, they're useful when two different
clients need to view an object differently.
13. Implementation
Implementing class adapters in C++. In a C++
implementation of a class adapter, Adapter would inherit
publicly from Target and privately from Adaptee. Thus
Adapter would be a subtype of Target but not of Adaptee.
Pluggable adapters.
Using abstract operations.
Using delegate objects.
Parameterized adapters.
14. Assignment
Your application domain needs a generic Stack<T> class. And your
development platform is C#.NET
Let’s assume .NET Framework doesn’t have a generic stack
implementation (actually, there is; but just assume there isn’t :D).
Also assume there is a generic queue implementation in C# (actually,
you don’t have to assume this time; there is in fact one such class
under System.Collections.Generic :D).
Write an adapter for this generic queue class to be useable as a
generic stack class. You must show both versions – class adapter and
object adapter. And your stack class should contain all the standard
stack operations.