📄 pat4a-1.htm
字号:
<UL>
<A NAME="auto1035"></A>
<LI>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.</LI>
<A NAME="auto1036"></A>
<P></P>
<A NAME="auto1037"></A>
<LI>makes it harder to override Adaptee behavior. It will require
subclassing Adaptee and making Adapter refer to the subclass
rather than the Adaptee itself.</LI>
</UL>
<A NAME="auto1038"></A>
<P>Here are other issues to consider when using the Adapter pattern:</P>
<OL>
<A NAME="pluggable"></A>
<LI><EM>How much adapting does Adapter do?</EM>
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.
The amount of work Adapter does depends on how similar the Target
interface is to Adaptee's.</LI>
<A NAME="auto1039"></A>
<P></P>
<A NAME="deleg-plug"></A>
<LI><EM>Pluggable adapters.</EM>
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. Put another way, interface adaptation lets us incorporate
our class into existing systems that might expect different interfaces
to the class.
ObjectWorks\Smalltalk [<A HREF="bibfs-1.htm#parcplace_smalltalk" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#parcplace_smalltalk" TARGET="_mainDisplayFrame">Par90</A>] uses the term
<STRONG>pluggable adapter</STRONG> to describe classes with built-in
interface adaptation.
<A NAME="treedisplay"></A>
<P>Consider a TreeDisplay widget that can display tree structures
graphically. If this were a special-purpose widget for use in just one
application, then we might require the objects that it displays to
have a specific interface; that is, all must descend from a Tree
abstract class. But if we wanted to make TreeDisplay more reusable
(say we wanted to make it part of a toolkit of useful widgets), then
that requirement would be unreasonable. Applications will define
their own classes for tree structures. They shouldn't be forced to
use our Tree abstract class. Different tree structures will have
different interfaces.</P>
<A NAME="auto1040"></A>
<P>In a directory hierarchy, for example, children might be accessed with
a GetSubdirectories operation, whereas in an inheritance hierarchy,
the corresponding operation might be called GetSubclasses. A reusable
TreeDisplay widget must be able to display both kinds of hierarchies
even if they use different interfaces. In other words, the
TreeDisplay should have interface adaptation built into it.</P>
<A NAME="auto1041"></A>
<P>We'll look at different ways to build interface adaptation into classes
in the Implementation section.</P>
</LI>
<A NAME="auto1042"></A>
<P></P>
<A NAME="twoway"></A>
<LI><EM>Using two-way adapters to provide transparency.</EM>
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.
<STRONG>Two-way adapters</STRONG> can provide such transparency.
Specifically, they're useful when two different clients need to view
an object differently.
<A NAME="qoca-use-adapt"></A>
<A NAME="unidraw-use-adapt"></A>
<P>Consider the two-way adapter that integrates Unidraw, a graphical
editor framework [<A HREF="bibfs-1.htm#unidraw_framework" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#unidraw_framework" TARGET="_mainDisplayFrame">VL90</A>], and QOCA, a
constraint-solving toolkit [<A HREF="bibfs-1.htm#qoca" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#qoca" TARGET="_mainDisplayFrame">HHMV92</A>]. Both systems have classes
that represent variables explicitly: Unidraw has StateVariable, and
QOCA has ConstraintVariable. To make Unidraw work with QOCA,
ConstraintVariable must be adapted to StateVariable; to let QOCA
propagate solutions to Unidraw, StateVariable must be adapted to
ConstraintVariable.</P>
<A NAME="143c"></A>
<P ALIGN=CENTER><IMG SRC="adapt107-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/Pictures/adapt107.gif"></P>
<A NAME="auto1043"></A>
<P>The solution involves a two-way class adapter ConstraintStateVariable,
a subclass of both StateVariable and ConstraintVariable, that adapts
the two interfaces to each other. Multiple inheritance is a viable
solution in this case because the interfaces of the adapted classes
are substantially different. The two-way class adapter conforms to
both of the adapted classes and can work in either system.</P>
</LI>
</OL>
<A NAME="implementation"></A>
<H2><A HREF="#samplecode"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0 ALT="next:
Sample Code"></A> Implementation</H2>
<A NAME="auto1044"></A>
<P>Although the implementation of Adapter is usually straightforward,
here are some issues to keep in mind:</P>
<OL>
<A NAME="auto1045"></A>
<LI><EM>Implementing class adapters in C++.</EM>
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.</LI>
<A NAME="auto1046"></A>
<P></P>
<A NAME="plugap-imp"></A>
<LI><EM>Pluggable adapters.</EM>
Let's look at three ways to implement pluggable adapters for the
TreeDisplay widget described earlier, which can lay out and display a
hierarchical structure automatically.
<A NAME="auto1047"></A>
<P>The first step, which is common to all three of the implementations
discussed here, is to find a "narrow" interface for Adaptee, that
is, the smallest subset of operations that lets us do the adaptation.
A narrow interface consisting of only a couple of operations is easier
to adapt than an interface with dozens of operations. For
TreeDisplay, the adaptee is any hierarchical structure. A minimalist
interface might include two operations, one that defines how to
present a node in the hierarchical structure graphically, and another
that retrieves the node's children.</P>
<A NAME="auto1048"></A>
<P>The narrow interface leads to three implementation approaches:</P>
<OL>
<A NAME="absops"></A>
<LI TYPE=a><EM>Using abstract operations.</EM>
Define corresponding abstract operations for the narrow Adaptee
interface in the TreeDisplay class. Subclasses must implement the
abstract operations and adapt the hierarchically structured object.
For example, a DirectoryTreeDisplay subclass will implement these
operations by accessing the directory structure.
<A NAME="adapter-param1"></A>
<P ALIGN=CENTER><IMG SRC="adapt103-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/Pictures/adapt103.gif"></P>
<A NAME="auto1049"></A>
<P>DirectoryTreeDisplay specializes the narrow interface so that it can
display directory structures made up of FileSystemEntity objects.</P>
</LI>
<A NAME="auto1050"></A>
<P></P>
<A NAME="use-dele"></A>
<LI TYPE=a><EM>Using delegate objects.</EM>
In this approach, TreeDisplay forwards requests for accessing the hierarchical
structure to a <STRONG>delegate</STRONG> object. TreeDisplay can use a
different adaptation strategy by substituting a different delegate.
<A NAME="direc-browse"></A>
<P>For example, suppose there exists a DirectoryBrowser that uses a
TreeDisplay. DirectoryBrowser might make a good delegate for
adapting TreeDisplay to the hierarchical directory structure. In
dynamically typed languages like Smalltalk or Objective C, this
approach only requires an interface for registering the delegate with
the adapter. Then TreeDisplay simply forwards the requests to the
delegate. NEXTSTEP [<A HREF="bibfs-1.htm#NeXT_AppKit" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#NeXT_AppKit" TARGET="_mainDisplayFrame">Add94</A>] uses this approach heavily to
reduce subclassing.</P>
<A NAME="treeaccdeleg"></A>
<P>Statically typed languages like C++ require an explicit interface
definition for the delegate. We can specify such an interface by
putting the narrow interface that TreeDisplay requires into an
abstract TreeAccessorDelegate class. Then we can mix this interface
into the delegate of our choice—DirectoryBrowser in this
case—using inheritance. We use single inheritance if the
DirectoryBrowser has no existing parent class, multiple inheritance if
it does. Mixing classes together like this is easier than introducing
a new TreeDisplay subclass and implementing its operations
individually.</P>
<A NAME="adapter-param2"></A>
<P ALIGN=CENTER><IMG SRC="adapt102-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/Pictures/adapt102.gif"></P>
</LI>
<A NAME="auto1051"></A>
<P></P>
<A NAME="parameterized"></A>
<LI TYPE=a><EM>Parameterized adapters.</EM>
The usual way to support pluggable adapters in Smalltalk is to
parameterize an adapter with one or more blocks. The block construct
supports adaptation without subclassing. A block can adapt a request,
and the adapter can store a block for each individual request. In our
example, this means TreeDisplay stores one block for converting a node
into a GraphicNode and another block for accessing a node's children.
<A NAME="auto1052"></A>
<P>For example, to create TreeDisplay on a directory hierarchy, we write</P>
<A NAME="auto1053"></A>
<PRE>
directoryDisplay :=
(TreeDisplay on: treeRoot)
getChildrenBlock:
[:node | node getSubdirectories]
createGraphicNodeBlock:
[:node | node createGraphicNode].
</PRE>
<A NAME="auto1054"></A>
<P>If you're building interface adaptation into a class, this approach
offers a convenient alternative to subclassing.</P>
</LI>
</OL>
</OL>
<A NAME="samplecode"><A>
<H2><A HREF="#knownuses"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0 ALT="next:
Known Uses"></A> Sample Code</H2>
<A NAME="auto1055"></A>
<P>We'll give a brief sketch of the implementation of class and object
adapters for the Motivation example beginning with the classes
<CODE>Shape</CODE> and <CODE>TextView</CODE>.</P>
<A NAME="auto1056"></A>
<PRE>
class Shape {
public:
Shape();
virtual void BoundingBox(
Point& bottomLeft, Point& topRight
) const;
virtual Manipulator* CreateManipulator() const;
};
class TextView {
public:
TextView();
void GetOrigin(Coord& x, Coord& y) const;
void GetExtent(Coord& width, Coord& height) const;
virtual bool IsEmpty() const;
};
</PRE>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -