📄 chap2.htm
字号:
<P>There are more sophisticated ways to select the factory at run-time.For example, you could maintain a registry that maps strings tofactory objects. That lets you register instances of new factorysubclasses without modifying existing code, as the preceding approach requires. And you don't have to link all platform-specific factoriesinto the application. That's important, because it might not bepossible to link a MotifFactory on a platform that doesn't supportMotif.</P><A NAME="auto1084"></A><P>But the point is that once we've configured the application with theright factory object, its look and feel is set from then on. If wechange our minds, we can reinitialize <CODE>guiFactory</CODE> with afactory for a different look and feel and then reconstruct theinterface. Regardless of how and when we decide to initialize<CODE>guiFactory</CODE>, we know that once we do, the application cancreate the appropriate look and feel without modification.</P><A NAME="absfact"></A><H3>Abstract Factory Pattern</H3><A NAME="auto1085"></A><P>Factories and products are the key participants in the <A HREF="pat3afs.htm" TARGET="_mainDisplayFrame">Abstract Factory (87)</A> pattern. This pattern captures howto create families of related product objects without instantiatingclasses directly. It's most appropriate when the number and generalkinds of product objects stay constant, and there are differences inspecific product families. We choose between families by instantiatinga particular concrete factory and using it consistently to createproducts thereafter. We can also swap entire families of products byreplacing the concrete factory with an instance of a different one.The Abstract Factory pattern's emphasis on <EM>families</EM> of productsdistinguishes it from other creational patterns, which involve only onekind of product object.</P><A NAME="sec2-6"></A><H2><A HREF="#sec2-7"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: User Operations"></A>Supporting Multiple Window Systems</H2><A NAME="auto1086"></A><P>Look and feel is just one of many portability issues. Another is thewindowing environment in which Lexi runs. A platform's window systemcreates the illusion of multiple overlapping windows on a bitmappeddisplay. It manages screen space for windows and routes input to them fromthe keyboard and mouse. Several important and largely incompatible windowsystems exist today (e.g., Macintosh, Presentation Manager, Windows, X).We'd like Lexi to run on as many of them as possible for exactly thesame reasons we support multiple look-and-feel standards.</P><H3>Can We Use an Abstract Factory?</H3><A NAME="auto1087"></A><P>At first glance this may look like another opportunity to apply theAbstract Factory pattern. But the constraints for window system portabilitydiffer significantly from those for look-and-feel independence.<A NAME="auto1088"></A><P>In applying the Abstract Factory pattern, we assumed we would definethe concrete widget glyph classes for each look-and-feel standard.That meant we could derive each concrete product for a particularstandard (e.g., MotifScrollBar and MacScrollBar) from an abstractproduct class (e.g., ScrollBar). But suppose we already have severalclass hierarchies from different vendors, one for each look-and-feelstandard. Of course, it's highly unlikely these hierarchies arecompatible in any way. Hence we won't have a common abstract productclass for each kind of widget (ScrollBar, Button, Menu, etc.)—and theAbstract Factory pattern won't work without those crucial classes.We have to make the different widget hierarchies adhere to a commonset of abstract product interfaces. Only then could we declare the<CODE>Create...</CODE> operations properly in our abstract factory'sinterface.</P><A NAME="auto1089"></A><P>We solved this problem for widgets by developing our own abstract andconcrete product classes. Now we're faced with a similar problem whenwe try to make Lexi work on existing window systems; namely,different window systems have incompatible programming interfaces.Things are a bit tougher this time, though, because we can't afford toimplement our own nonstandard window system.</P><A NAME="auto1090"></A><P>But there's a saving grace. Like look-and-feel standards, windowsystem interfaces aren't radically different from one another, becauseall window systems do generally the same thing. We need a uniform setof windowing abstractions that lets us take different window systemimplementations and slide any one of them under a common interface.</P><H3>Encapsulating Implementation Dependencies</H3><A NAME="auto1091"></A><P>In <A HREF="#editor_sec_document_structure">Section 2.2</A>we introduced a Window class for displaying a glyph or glyphstructure on the display. We didn't specify the window system thatthis object worked with, because the truth is that it doesn't comefrom any particular window system. The Window class encapsulatesthe things windows tend to do across window systems:</P><UL><A NAME="auto1092"></A><LI>They provide operations for drawing basic geometric shapes.</LI><A NAME="auto1093"></A><P></P><A NAME="auto1094"></A><LI>They can iconify and de-iconify themselves.</LI><A NAME="auto1095"></A><P></P><A NAME="auto1096"></A><LI>They can resize themselves.</LI><A NAME="auto1097"></A><P></P><A NAME="auto1098"></A><LI>They can (re)draw their contents on demand, for example, when theyare de-iconified or when an overlapped and obscured portion of theirscreen space is exposed.</LI></UL><A NAME="auto1099"></A><P>The Window class must span the functionality of windows from differentwindow systems. Let's consider two extreme philosophies:</P><OL><A NAME="auto1100"></A><LI><EM>Intersection of functionality.</EM>The Window class interface provides only functionality that's commonto <EM>all</EM> window systems. The problem with this approach is thatour Window interface winds up being only as powerful as the leastcapable window system. We can't take advantage of more advancedfeatures even if most (but not all) window systems support them.</LI><A NAME="auto1101"></A><P></P><A NAME="auto1102"></A><LI><EM>Union of functionality.</EM>Create an interface that incorporates the capabilities of <EM>all</EM>existing systems. The trouble here is that the resulting interface maywell be huge and incoherent. Besides, we'll have to change it (andLexi, which depends on it) anytime a vendor revises its windowsystem interface.</LI></OL><A NAME="auto1103"></A><P>Neither extreme is a viable solution, so our design will fallsomewhere between the two. The Window class will provide a convenientinterface that supports the most popular windowing features. BecauseLexi will deal with this class directly, the Window class must alsosupport the things Lexi knows about, namely, glyphs. That meansWindow's interface must include a basic set of graphics operationsthat lets glyphs draw themselves in the window.<A HREF="#editor_window_base_class_interface">Table 2.3</A>gives a sampling of the operations in the Window class interface.</P><CENTER><TABLE BORDER = 1 CELLPADDING = 4 CELLSPACING = 0 BGCOLOR = #99CCFF><A NAME="editor_window_base_class_interface"></A><TR><TH BGCOLOR=#6699CC>Responsibility</TH><TH BGCOLOR=#6699CC>Operations</TH></TR><A NAME="auto1104"></A><TR><TD VALIGN=TOP>window management</TD><TD><CODE>virtual void Redraw()</CODE><BR><CODE>virtual void Raise()</CODE><BR><CODE>virtual void Lower()</CODE><BR><CODE>virtual void Iconify()</CODE><BR><CODE>virtual void Deiconify()</CODE><BR><CODE>...</CODE></TD></TR><A NAME="auto1105"></A><TR><TD VALIGN=TOP>graphics</TD><TD><CODE>virtual void DrawLine(...)</CODE><BR><CODE>virtual void DrawRect(...)</CODE><BR><CODE>virtual void DrawPolygon(...)</CODE><BR><CODE>virtual void DrawText(...)</CODE><BR><CODE>...</CODE></TD></TR></TABLE></CENTER><P ALIGN=CENTER>Table 2.3: Window class interface</P><A NAME="appwin"></A><P>Window is an abstract class. Concrete subclasses of Window support thedifferent kinds of windows that users deal with. For example,application windows, icons, and warning dialogs are all windows, butthey have somewhat different behaviors. So we can define subclasseslike ApplicationWindow, IconWindow, and DialogWindow to capture thesedifferences. The resulting class hierarchy gives applications likeLexi a uniform and intuitive windowing abstraction, one that doesn'tdepend on any particular vendor's window system:</P><A NAME="54c"></A><P ALIGN=CENTER><IMG SRC="Pictures/windo001.gif"></P><A NAME="auto1106"></A><P>Now that we've defined a window interface for Lexi to work with,where does the real platform-specific window come in? If we're notimplementing our own window system, then at some point our windowabstraction must be implemented in terms of what the target windowsystem provides. So where does that implementation live?</P><A NAME="auto1107"></A><P>One approach is to implement multiple versions of the Window class andits subclasses, one version for each windowing platform. We'd have tochoose the version to use when we build Lexi for a given platform.But imagine the maintenance headaches we'd have keeping track ofmultiple classes, all named "Window" but each implemented on adifferent window system. Alternatively, we could createimplementation-specific subclasses of each class in the Windowhierarchy—and end up with another subclass explosion problem like the onewe had trying to add embellishments. Both of these alternatives haveanother drawback: Neither gives us the flexibility to change thewindow system we use after we've compiled the program. So we'll haveto keep several different executables around as well.</P><A NAME="encap-var-concept"></A><P>Neither alternative is very appealing, but what else can we do? Thesame thing we did for formatting and embellishment, namely, <EM>encapsulate the concept that varies</EM>. What varies in this case is thewindow system implementation. If we encapsulate a window system'sfunctionality in an object, then we can implement our Window class andsubclasses in terms of that object's interface. Moreover, if thatinterface can serve all the window systems we're interested in, thenwe won't have to change Window or any of its subclasses to supportdifferent window systems. We can configure window objects to thewindow system we want simply by passing them the right windowsystem-encapsulating object. We can even configure the window atrun-time.</P><A NAME="windowimp"></A><A NAME="windowimp-subclass"></A><H3>Window and WindowImp</H3><A NAME="auto1108"></A><P>We'll define a separate <STRONG>WindowImp</STRONG> class hierarchy in which tohide different window system implementations. WindowImp is an abstractclass for objects that encapsulate window system-dependent code. To makeLexi work on a particular window system, we configure each windowobject with an instance of a WindowImp subclass for that system. Thefollowing diagram shows the relationship between the Window and WindowImphierarchies:</P><A NAME="55c"></A><P ALIGN=CENTER><IMG SRC="Pictures/windo111.gif"></P><A NAME="auto1109"></A><P>By hiding the implementations in WindowImp classes, we avoid pollutingthe Window classes with window system dependencies, which keeps theWindow class hierarchy comparatively small and stable. Meanwhile wecan easily extend the implementation hierarchy to support new windowsystems.</P><H3>WindowImp Subclasses</H3><A NAME="auto1110"></A><P>Subclasses of WindowImp convert requests into window system-specificoperations. Consider the example we used in<A HREF="#sec2-2">Section 2.2</A>. We defined the<CODE>Rectangle::Draw</CODE> in terms of the <CODE>DrawRect</CODE> operation onthe Window instance:</P><A NAME="auto1111"></A><PRE> void Rectangle::Draw (Window* w) { w->DrawRect(_x0, _y0, _x1, _y1); }</PRE><A NAME="auto1112"></A><P>The default implementation of <CODE>DrawRect</CODE> uses the abstractoperation for drawing rectangles declared by WindowImp:</P><A NAME="auto1113"></A><PRE> void Window::DrawRect ( Coord x0, Coord y0, Coord x1, Coord y1 ) { _imp->DeviceRect(x0, y0, x1, y1); }</PRE><A NAME="xwindows"></A><P>where <CODE>_imp</CODE> is a member variable of Window that stores theWindowImp with which the Window is configured. The windowimplementation is defined by the instance of the WindowImp subclassthat <CODE>_imp</CODE> points to. For an XWindowImp (that is, aWindowImp subclass for the X Window System), the<CODE>DeviceRect</CODE>'s implementation might look like<A NAME="auto1114"></A><PRE> void XWindowImp::DeviceRect (
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -