📄 pat4a.htm
字号:
<SCRIPT>
function setFocus() {
if ((navigator.appName != "Netscape") && (parseFloat(navigator.appVersion) == 2)) {
return;
} else {
self.focus();
}
}
</SCRIPT><HTML><HEAD><TITLE>Adapter</TITLE></HEAD><BODY BGCOLOR = #FFFFFF TEXT = #000000
onLoad="setFocus()";><A NAME="top"></A><A NAME="Adapter"></A><A NAME="intent"></A><H2><A HREF="#alsoknownas"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Also Known As"></A> Intent</H2> <A NAME="auto1000"></A><P>Convert the interface of a class into another interface clientsexpect. Adapter lets classes work together that couldn't otherwisebecause of incompatible interfaces.</P><A NAME="alsoknownas"><A><H2><A HREF="#motivation"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Motivation"></A> Also Known As</H2> <A NAME="auto1001"></A><P>Wrapper</P><A NAME="motivation"></A><H2><A HREF="#applicability"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Applicability"></A> Motivation</H2> <A NAME="auto1002"></A><P>Sometimes a toolkit class that's designed for reuse isn't reusableonly because its interface doesn't match the domain-specific interfacean application requires.</P><A NAME="auto1003"></A><P>Consider for example a drawing editor that lets users draw and arrangegraphical elements (lines, polygons, text, etc.) into pictures anddiagrams. The drawing editor's key abstraction is the graphicalobject, which has an editable shape and can draw itself. Theinterface for graphical objects is defined by an abstract class calledShape. The editor defines a subclass of Shape for each kind ofgraphical object: a LineShape class for lines, a PolygonShape classfor polygons, and so forth.</P><A NAME="textshape"></A><A NAME="textview"></A><P>Classes for elementary geometric shapes like LineShape andPolygonShape are rather easy to implement, because their drawing andediting capabilities are inherently limited. But a TextShape subclassthat can display and edit text is considerably more difficult toimplement, since even basic text editing involves complicated screenupdate and buffer management. Meanwhile, an off-the-shelf userinterface toolkit might already provide a sophisticated TextView classfor displaying and editing text. Ideally we'd like to reuse TextViewto implement TextShape, but the toolkit wasn't designed with Shapeclasses in mind. So we can't use TextView and Shape objectsinterchangeably.</P><A NAME="auto1004"></A><P>How can existing and unrelated classes like TextView work in anapplication that expects classes with a different and incompatibleinterface? We could change the TextView class so that it conforms tothe Shape interface, but that isn't an option unless we have thetoolkit's source code. Even if we did, it wouldn't make sense tochange TextView; the toolkit shouldn't have to adopt domain-specificinterfaces just to make one application work.</P><A NAME="adapterdef"></A><P>Instead, we could define TextShape so that it <EM>adapts</EM> theTextView 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 implementingTextShape in terms of TextView's interface. These two approachescorrespond to the class and object versions of the Adapter pattern.We call TextShape an <STRONG>adapter</STRONG>.</P><A NAME="shape-140c"></A><P ALIGN=CENTER><IMG SRC="Pictures/adapt105.gif"></P><A NAME="auto1005"></A><P>This diagram illustrates the object adapter case. It shows howBoundingBox requests, declared in class Shape, are converted toGetExtent requests defined in TextView. Since TextShape adaptsTextView to the Shape interface, the drawing editor can reuse theotherwise incompatible TextView class.</P><A NAME="auto1006"></A><P>Often the adapter is responsible for functionality the adapted classdoesn't provide. The diagram shows how an adapter can fulfillsuch responsibilities. The user should be able to "drag" everyShape object to a new location interactively, but TextView isn'tdesigned to do that. TextShape can add this missing functionality byimplementing Shape's CreateManipulator operation, which returns aninstance of the appropriate Manipulator subclass.</P><A NAME="auto1007"></A><P>Manipulator is an abstract class for objects that know how to animatea Shape in response to user input, like dragging the shape to a newlocation. There are subclasses of Manipulator for different shapes;TextManipulator, for example, is the corresponding subclass forTextShape. By returning a TextManipulator instance, TextShape addsthe functionality that TextView lacks but Shape requires.</P><A NAME="applicability"></A><H2><A HREF="#structure"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Structure"></A> Applicability</H2> <A NAME="auto1008"></A><P>Use the Adapter pattern when</P><UL><A NAME="auto1009"></A><LI>you want to use an existing class, and its interface does notmatch the one you need.</P><A NAME="auto1010"></A><LI>you want to create a reusable class that cooperates with unrelated orunforeseen classes, that is, classes that don't necessarily havecompatible interfaces.</P><A NAME="auto1011"></A><LI><EM>(object adapter only)</EM>you need to use several existing subclasses, but it's impractical toadapt their interface by subclassing every one. An object adapter canadapt the interface of its parent class.</P></UL><A NAME="structure"></A><H2><A HREF="#participants"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Participants"></A> Structure</H2> <A NAME="141c"></A><P>A class adapter uses multiple inheritance to adapt one interfaceto another:</P><P ALIGN=CENTER><IMG SRC="Pictures/adapt106.gif"></P><A NAME="141o"></A><P>An object adapter relies on object composition:</P><P ALIGN=CENTER><IMG SRC="Pictures/adapt104.gif"></P><A NAME="participants"></A><H2><A HREF="#collaborations"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Collaborations"></A> Participants</H2><UL><A NAME="auto1012"></A><LI><B>Target</B> (Shape)<A NAME="auto1013"></A><P></P><UL> <A NAME="auto1014"></A><LI>defines the domain-specific interface that Client uses.</LI></UL><A NAME="auto1015"></A><P></P><A NAME="auto1016"></A><LI><B>Client</B> (DrawingEditor)<A NAME="auto1017"></A><P></P><UL> <A NAME="auto1018"></A><LI>collaborates with objects conforming to the Target interface.</LI></UL><A NAME="auto1019"></A><P></P><A NAME="auto1020"></A><LI><B>Adaptee</B> (TextView)<A NAME="auto1021"></A><P></P><UL> <A NAME="auto1022"></A><LI>defines an existing interface that needs adapting.</LI></UL><A NAME="auto1023"></A><P></P><A NAME="auto1024"></A><LI><B>Adapter</B> (TextShape)<A NAME="auto1025"></A><P></P><UL> <A NAME="auto1026"></A><LI>adapts the interface of Adaptee to the Target interface.</LI></UL></UL><A NAME="collaborations"></A><H2><A HREF="#consequences"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Consequences"></A> Collaborations</H2><UL><A NAME="auto1027"></A><LI>Clients call operations on an Adapter instance. In turn, the adaptercalls Adaptee operations that carry out the request.</LI></UL><A NAME="consequences"></A><H2><A HREF="#implementation"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Implementation"></A> Consequences</H2> <A NAME="auto1028"></A><P>Class and object adapters have different trade-offs. A class adapter</P><UL><A NAME="auto1029"></A><LI>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 aclass <EM>and</EM> all its subclasses.</LI><A NAME="auto1030"></A><P></P><A NAME="auto1031"></A><LI>lets Adapter override some of Adaptee's behavior, sinceAdapter is a subclass of Adaptee.</LI><A NAME="auto1032"></A><P></P><A NAME="auto1033"></A><LI>introduces only one object, and no additional pointer indirection isneeded to get to the adaptee.</LI></UL><A NAME="auto1034"></A><P>An object adapter</P><UL><A NAME="auto1035"></A><LI>lets a single Adapter work with many Adaptees—that is, the Adapteeitself and all of its subclasses (if any). The Adapter can also addfunctionality 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 requiresubclassing Adaptee and making Adapter refer to the subclassrather 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 tothe Target interface. There is a spectrum of possible work, fromsimple interface conversion—for example, changing the names ofoperations—to supporting an entirely different set of operations.The amount of work Adapter does depends on how similar the Targetinterface 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 otherclasses must make to use it. By building interface adaptation into aclass, you eliminate the assumption that other classes see the sameinterface. Put another way, interface adaptation lets us incorporateour class into existing systems that might expect different interfacesto the class.ObjectWorks\Smalltalk [<A HREF="bibfs.htm#parcplace_smalltalk" TARGET="_mainDisplayFrame">Par90</A>] uses the term<STRONG>pluggable adapter</STRONG> to describe classes with built-ininterface adaptation.<A NAME="treedisplay"></A><P>Consider a TreeDisplay widget that can display tree structuresgraphically. If this were a special-purpose widget for use in just oneapplication, then we might require the objects that it displays tohave a specific interface; that is, all must descend from a Treeabstract class. But if we wanted to make TreeDisplay more reusable(say we wanted to make it part of a toolkit of useful widgets), thenthat requirement would be unreasonable. Applications will definetheir own classes for tree structures. They shouldn't be forced touse our Tree abstract class. Different tree structures will havedifferent interfaces.</P><A NAME="auto1040"></A><P>In a directory hierarchy, for example, children might be accessed witha GetSubdirectories operation, whereas in an inheritance hierarchy,the corresponding operation might be called GetSubclasses. A reusableTreeDisplay widget must be able to display both kinds of hierarchieseven if they use different interfaces. In other words, theTreeDisplay should have interface adaptation built into it.</P><A NAME="auto1041"></A><P>We'll look at different ways to build interface adaptation into classesin 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 toall clients. An adapted object no longer conforms to the Adapteeinterface, 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 viewan object differently.<A NAME="qoca-use-adapt"></A><A NAME="unidraw-use-adapt"></A><P>Consider the two-way adapter that integrates Unidraw, a graphicaleditor framework [<A HREF="bibfs.htm#unidraw_framework" TARGET="_mainDisplayFrame">VL90</A>], and QOCA, aconstraint-solving toolkit [<A HREF="bibfs.htm#qoca" TARGET="_mainDisplayFrame">HHMV92</A>]. Both systems have classesthat represent variables explicitly: Unidraw has StateVariable, andQOCA has ConstraintVariable. To make Unidraw work with QOCA,ConstraintVariable must be adapted to StateVariable; to let QOCApropagate solutions to Unidraw, StateVariable must be adapted toConstraintVariable.</P><A NAME="143c"></A><P ALIGN=CENTER><IMG SRC="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 adaptsthe two interfaces to each other. Multiple inheritance is a viablesolution in this case because the interfaces of the adapted classesare substantially different. The two-way class adapter conforms toboth of the adapted classes and can work in either system.</P></LI></OL><A NAME="implementation"></A><H2><A HREF="#samplecode"><IMG SRC="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, Adapterwould 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 theTreeDisplay widget described earlier, which can lay out and display ahierarchical structure automatically.<A NAME="auto1047"></A><P>The first step, which is common to all three of the implementationsdiscussed here, is to find a "narrow" interface for Adaptee, that
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -