📄 pat4a-1.htm
字号:
<A NAME="textshape2"></A>
<P><CODE>Shape</CODE> assumes a bounding box defined by its opposing
corners. In contrast, <CODE>TextView</CODE> is defined by an origin,
height, and width. <CODE>Shape</CODE> also defines a
<CODE>CreateManipulator</CODE> operation for creating a
<CODE>Manipulator</CODE> object, which knows how to animate a shape
when the user manipulates it.<A NAME="fn1"></A><A
HREF="#footnote1"><SUP>1</SUP></A> <CODE>TextView</CODE> has no
equivalent operation. The class <CODE>TextShape</CODE> is an
adapter between these different interfaces.</P>
<A NAME="auto1057"></A>
<P>A class adapter uses multiple inheritance to adapt interfaces. The key
to class adapters is to use one inheritance branch to inherit the
interface and another branch to inherit the implementation. The usual
way to make this distinction in C++ is to inherit the interface
publicly and inherit the implementation privately. We'll use this
convention to define the <CODE>TextShape</CODE> adapter.</P>
<A NAME="auto1058"></A>
<PRE>
class TextShape : public Shape, private TextView {
public:
TextShape();
virtual void BoundingBox(
Point& bottomLeft, Point& topRight
) const;
virtual bool IsEmpty() const;
virtual Manipulator* CreateManipulator() const;
};
</PRE>
<A NAME="auto1059"></A>
<P>The <CODE>BoundingBox</CODE> operation converts <CODE>TextView</CODE>'s
interface to conform to <CODE>Shape</CODE>'s.</P>
<A NAME="auto1060"></A>
<PRE>
void TextShape::BoundingBox (
Point& bottomLeft, Point& topRight
) const {
Coord bottom, left, width, height;
GetOrigin(bottom, left);
GetExtent(width, height);
bottomLeft = Point(bottom, left);
topRight = Point(bottom + height, left + width);
}
</PRE>
<A NAME="auto1061"></A>
<P>The <CODE>IsEmpty</CODE> operation demonstrates the direct forwarding of
requests common in adapter implementations:</P>
<A NAME="auto1062"></A>
<PRE>
bool TextShape::IsEmpty () const {
return TextView::IsEmpty();
}
</PRE>
<A NAME="auto1063"></A>
<P>Finally, we define <CODE>CreateManipulator</CODE> (which isn't supported
by <CODE>TextView</CODE>) from scratch. Assume we've already implemented
a <CODE>TextManipulator</CODE> class that supports manipulation of a
<CODE>TextShape</CODE>.</P>
<A NAME="auto1064"></A>
<PRE>
Manipulator* TextShape::CreateManipulator () const {
return new TextManipulator(this);
}
</PRE>
<A NAME="auto1065"></A>
<P>The object adapter uses object composition to combine classes with
different interfaces. In this approach, the adapter
<CODE>TextShape</CODE> maintains a pointer to
<CODE>TextView</CODE>.</P>
<A NAME="auto1066"></A>
<PRE>
class TextShape : public Shape {
public:
TextShape(TextView*);
virtual void BoundingBox(
Point& bottomLeft, Point& topRight
) const;
virtual bool IsEmpty() const;
virtual Manipulator* CreateManipulator() const;
private:
TextView* _text;
};
</PRE>
<A NAME="textshape3"></A>
<P><CODE>TextShape</CODE> must initialize the pointer to the
<CODE>TextView</CODE> instance, and it does so in the constructor. It
must also call operations on its <CODE>TextView</CODE> object whenever
its own operations are called. In this example, assume that the client
creates the <CODE>TextView</CODE> object and passes it to the
<CODE>TextShape</CODE> constructor:</P>
<A NAME="auto1067"></A>
<PRE>
TextShape::TextShape (TextView* t) {
_text = t;
}
void TextShape::BoundingBox (
Point& bottomLeft, Point& topRight
) const {
Coord bottom, left, width, height;
_text->GetOrigin(bottom, left);
_text->GetExtent(width, height);
bottomLeft = Point(bottom, left);
topRight = Point(bottom + height, left + width);
}
bool TextShape::IsEmpty () const {
return _text->IsEmpty();
}
</PRE>
<A NAME="et-use-adapter"></A>
<P><CODE>CreateManipulator</CODE>'s implementation doesn't change
from the class adapter version, since it's implemented from scratch
and doesn't reuse any existing <CODE>TextView</CODE> functionality.</P>
<A NAME="auto1068"></A>
<PRE>
Manipulator* TextShape::CreateManipulator () const {
return new TextManipulator(this);
}
</PRE>
<A NAME="auto1069"></A>
<P>Compare this code to the class adapter case. The object adapter
requires a little more effort to write, but it's more flexible. For
example, the object adapter version of <CODE>TextShape</CODE> will work
equally well with subclasses of <CODE>TextView</CODE>—the client simply
passes an instance of a <CODE>TextView</CODE> subclass to the
<CODE>TextShape</CODE> constructor.</P>
<A NAME="knownuses"><A>
<H2><A HREF="#relatedpatterns"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0
ALT="next: Related Patterns"></A> Known Uses</H2>
<A NAME="auto1070"></A>
<P>The Motivation example comes from ET++Draw, a drawing application
based on ET++ [<A HREF="bibfs-1.htm#et++" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#et++" TARGET="_mainDisplayFrame">WGM88</A>]. ET++Draw
reuses the ET++ classes for text editing by using a TextShape
adapter class.</P>
<A NAME="auto1071"></A>
<P>InterViews 2.6 defines an Interactor abstract class for user
interface elements such as scroll bars, buttons, and menus [<A HREF="bibfs-1.htm#interviews_graphic" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#interviews_graphic" TARGET="_mainDisplayFrame">VL88</A>]. It also defines a
Graphic abstract class for structured graphic objects such as lines,
circles, polygons, and splines. Both Interactors and Graphics have
graphical appearances, but they have different interfaces and
implementations (they share no common parent class) and are therefore
incompatible—you can't embed a structured graphic object in,
say, a dialog box directly.</P>
<A NAME="auto1072"></A>
<P>Instead, InterViews 2.6 defines an object adapter called
GraphicBlock, a subclass of Interactor that contains a Graphic
instance. The GraphicBlock adapts the interface of the Graphic
class to that of Interactor. The GraphicBlock lets a Graphic
instance be displayed, scrolled, and zoomed within an Interactor
structure.</P>
<A NAME="plugap-imp2"></A>
<P>Pluggable adapters are common in
ObjectWorks\Smalltalk [<A HREF="bibfs-1.htm#parcplace_smalltalk" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#parcplace_smalltalk" TARGET="_mainDisplayFrame">Par90</A>]. Standard Smalltalk defines
a ValueModel class for views that display a single value. ValueModel
defines a <CODE>value</CODE>, <CODE>value:</CODE> interface for accessing
the value. These are abstract methods. Application writers access the
value with more domain-specific names like <CODE>width</CODE> and
<CODE>width:</CODE>, but they shouldn't have to subclass ValueModel
to adapt such application-specific names to the ValueModel interface.</P>
<A NAME="auto1073"></A>
<P>Instead, ObjectWorks\Smalltalk includes a subclass of ValueModel
called PluggableAdaptor. A PluggableAdaptor object adapts other
objects to the ValueModel interface (<CODE>value</CODE>,
<CODE>value:</CODE>). It can be parameterized with blocks for getting
and setting the desired value. PluggableAdaptor uses these blocks
internally to implement the
<CODE>value</CODE>, <CODE>value:</CODE> interface. PluggableAdaptor also
lets you pass in the selector names (e.g., <CODE>width</CODE>,
<CODE>width:</CODE>) directly for syntactic convenience.
It converts these selectors into the corresponding blocks
automatically.</P>
<A NAME="plugap-149c"></A>
<P ALIGN=CENTER><IMG SRC="plugg021-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/Pictures/plugg021.gif"></P>
<A NAME="auto1074"></A>
<P>Another example from ObjectWorks\Smalltalk is the TableAdaptor
class. A TableAdaptor can adapt a sequence of objects to a tabular
presentation. The table displays one object per row. The client
parameterizes TableAdaptor with the set of messages that a table can
use to get the column values from an object.</P>
<A NAME="auto1075"></A>
<P>Some classes in NeXT's AppKit [<A HREF="bibfs-1.htm#NeXT_AppKit" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#NeXT_AppKit" TARGET="_mainDisplayFrame">Add94</A>] use delegate objects
to perform interface adaptation. An example is the NXBrowser class that
can display hierarchical lists of data. NXBrowser uses a delegate
object for accessing and adapting the data.</P>
<A NAME="marriage"></A>
<P>Meyer's "Marriage of Convenience" [<A HREF="bibfs-1.htm#meyer_book-88" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#meyer_book-88" TARGET="_mainDisplayFrame">Mey88</A>] is a form of
class adapter. Meyer describes how a FixedStack class adapts the
implementation of an Array class to the interface of a Stack class.
The result is a stack containing a fixed number of entries.</P>
<A NAME="relatedpatterns"></A>
<H2><A HREF="#last"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0 ALT="next:
navigation"></A> Related Patterns</H2>
<A NAME="auto1076"></A>
<P><A HREF="pat4bfs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat4bfs.htm" TARGET="_mainDisplayFrame">Bridge (151)</A> has
a structure similar to an object adapter, but Bridge has a different
intent: It is meant to separate an interface from its implementation
so that they can be varied easily and independently. An adapter
is meant to change the interface of an <EM>existing</EM> object.</P>
<A NAME="auto1077"></A>
<P><A HREF="pat4dfs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat4dfs.htm" TARGET="_mainDisplayFrame">Decorator (175)</A>
enhances another object without changing its interface. A decorator
is thus more transparent to the application than an adapter is. As
a consequence, Decorator supports recursive composition, which
isn't possible with pure adapters.</P>
<A NAME="auto1078"></A>
<P><A HREF="pat4gfs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat4gfs.htm" TARGET="_mainDisplayFrame">Proxy (207)</A> defines
a representative or surrogate for another object and does not change
its interface.</P>
<A NAME="last"></A>
<P><A HREF="#intent"><IMG SRC="up3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/up3.gif" BORDER=0></A><BR>
<A HREF="pat4bfs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat4bfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="rightar3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/rightar3.gif"
ALIGN=TOP BORDER=0></A> <A HREF="pat4bfs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat4bfs.htm"
TARGET="_mainDisplayFrame">Bridge</A><BR>
<A HREF="chap4fs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/chap4fs.htm" TARGET="_mainDisplayFrame"><IMG SRC="leftarr3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/leftarr3.gif"
ALIGN=TOP BORDER=0></A> <A HREF="chap4fs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/chap4fs.htm"
TARGET="_mainDisplayFrame">Structural Patterns</A>
</P>
<HR>
<A NAME="footnote1"></A>
<P><SUP>1</SUP><CODE>CreateManipulator</CODE> is an example of a
<A HREF="pat3cfs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat3cfs.htm" TARGET="_mainDisplayFrame">Factory Method (107)</A>.
</P>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -