⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chap2.htm

📁 Design Pattern 设计模式
💻 HTM
📖 第 1 页 / 共 5 页
字号:
        Coord x0, Coord y0, Coord x1, Coord y1    ) {        int x = round(min(x0, x1));        int y = round(min(y0, y1));        int w = round(abs(x0 - x1));        int h = round(abs(y0 - y1));        XDrawRectangle(_dpy, _winid, _gc, x, y, w, h);    }</PRE><A NAME="auto1115"></A><P><CODE>DeviceRect</CODE> is defined like this because<CODE>XDrawRectangle</CODE> (the X interface for drawing a rectangle)defines a rectangle in terms of its lower left corner, its width,and its height.  <CODE>DeviceRect</CODE> must compute these valuesfrom those supplied.  First it ascertains the lower left corner(since (<CODE>x0</CODE>, <CODE>y0</CODE>) might be any oneof the rectangle's four corners) and then calculates the width andheight.</P><A NAME="present-manage"></A><P>PMWindowImp (a subclass of WindowImp for Presentation Manager) woulddefine <CODE>DeviceRect</CODE> differently:</P><A NAME="auto1116"></A><PRE>    void PMWindowImp::DeviceRect (        Coord x0, Coord y0, Coord x1, Coord y1    ) {        Coord left = min(x0, x1);        Coord right = max(x0, x1);        Coord bottom = min(y0, y1);        Coord top = max(y0, y1);            PPOINTL point[4];            point[0].x = left;    point[0].y = top;        point[1].x = right;   point[1].y = top;        point[2].x = right;   point[2].y = bottom;        point[3].x = left;    point[3].y = bottom;            if (            (GpiBeginPath(_hps, 1L) == false) ||            (GpiSetCurrentPosition(_hps, &amp;point[3]) == false) ||            (GpiPolyLine(_hps, 4L, point) == GPI_ERROR)  ||            (GpiEndPath(_hps) == false)        ) {            // report error            } else {            GpiStrokePath(_hps, 1L, 0L);        }    }</PRE><A NAME="path-multiseg-shape"></A><A NAME="present-manage2"></A><A NAME="xwind-differ"></A><P>Why is this so different from the X version? Well, PM doesn't have anoperation for drawing rectangles explicitly as X does. Instead, PM has amore general interface for specifying vertices of multisegment shapes(called a <STRONG>path</STRONG>) and for outlining or filling the area theyenclose.</P><A NAME="auto1117"></A><P>PM's implementation of <CODE>DeviceRect</CODE> is obviously quitedifferent from X's, but that doesn't matter.  WindowImp hidesvariations in window system interfaces behind a potentially large butstable interface.  That lets Window subclass writers focus on the windowabstraction and not on window system details.  It also lets us addsupport for new window systems without disturbing the Window classes.</P><A NAME="window-config-windowimp"></A><H3>Configuring Windows with WindowImps</H3><A NAME="auto1118"></A><P>A key issue we haven't addressed is how a window gets configured withthe proper WindowImp subclass in the first place.  Stated another way,when does <CODE>_imp</CODE> get initialized, and who knows what windowsystem (and consequently which WindowImp subclass) is in use?  Thewindow will need some kind of WindowImp before it can do anythinginteresting.</P><A NAME="windowsystfact"></A><P>There are several possibilities, but we'll focus on one that uses the<A HREF="pat3afs.htm" TARGET="_mainDisplayFrame">Abstract Factory (87)</A> pattern.  We can definean abstract factory class WindowSystemFactory that provides aninterface for creating different kinds of window system-dependentimplementation objects:</P><A NAME="auto1119"></A><PRE>    class WindowSystemFactory {    public:        virtual WindowImp* CreateWindowImp() = 0;        virtual ColorImp* CreateColorImp() = 0;        virtual FontImp* CreateFontImp() = 0;            // a "Create..." operation for all window system resources    };</PRE><A NAME="auto1120"></A><P>Now we can define a concrete factory for each window system:</P><A NAME="auto1121"></A><PRE>    class PMWindowSystemFactory : public WindowSystemFactory {        virtual WindowImp* CreateWindowImp()            { return new PMWindowImp; }        // ...    };        class XWindowSystemFactory : public WindowSystemFactory {        virtual WindowImp* CreateWindowImp()            { return new XWindowImp; }        // ...    };</PRE><A NAME="auto1122"></A><P>The Window base class constructor can use the<CODE>WindowSystemFactory</CODE> interface to initialize the<CODE>_imp</CODE> member with the WindowImp that's right for the windowsystem:</P><A NAME="auto1123"></A><PRE>    Window::Window () {        _imp = windowSystemFactory->CreateWindowImp();    }</PRE><A NAME="auto1124"></A><P>The <CODE>windowSystemFactory</CODE> variable is a well-known instance ofa WindowSystemFactory subclass, akin to the well-known<CODE>guiFactory</CODE> variable defining the look and feel.  The<CODE>windowSystemFactory</CODE> variable can be initialized in the sameway.</P><H3>Bridge Pattern</H3><A NAME="auto1125"></A><P>The WindowImp class defines an interface to common window systemfacilities, but its design is driven by different constraints thanWindow's interface.  Application programmers won't deal withWindowImp's interface directly; they only deal with Window objects.So WindowImp's interface needn't match the application programmer'sview of the world, as was our concern in the design of the Windowclass hierarchy and interface.  WindowImp's interface can more closelyreflect what window systems actually provide, warts and all. It can bebiased toward either an intersection or a union of functionalityapproach, whichever suits the target window systems best.</P><A NAME="auto1126"></A><P>The important thing to realize is that Window's interface caters tothe applications programmer, while WindowImp caters to window systems.Separating windowing functionality into Window and WindowImphierarchies lets us implement and specialize these interfacesindependently. Objects from these hierarchies cooperate to letLexi work without modification on multiple window systems.</P><A NAME="auto1127"></A><P>The relationship between Window and WindowImp is an example of the<A HREF="pat4bfs.htm" TARGET="_mainDisplayFrame">Bridge (151)</A> pattern. The intent behind Bridge is to allowseparate class hierarchies to work together even as they evolveindependently. Our design criteria led us to create two separate classhierarchies, one that supports the logical notion of windows, andanother for capturing different implementations of windows. The Bridgepattern lets us maintain and enhance our logical windowingabstractions without touching window system-dependent code, and viceversa.</P><A NAME="sec2-7"></A><H2><A HREF="#sec2-8"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Spelling Checking and Hyphenation"></A>User Operations</H2><A NAME="auto1128"></A><P>Some of Lexi's functionality is available through the document'sWYSIWYG representation.  You enter and delete text, move the insertionpoint, and select ranges of text by pointing, clicking, and typingdirectly in the document.  Other functionality is accessed indirectlythrough user operations in Lexi's pull-down menus, buttons, andkeyboard accelerators.  The functionality includes operations for</P><UL><A NAME="auto1129"></A><LI>creating a new document,<A NAME="auto1130"></A><LI>opening, saving, and printing an existing document,<A NAME="auto1131"></A><LI>cutting selected text out of the document and pasting it back in,<A NAME="auto1132"></A><LI>changing the font and style of selected text,<A NAME="auto1133"></A><LI>changing the formatting of text, such as its alignment andjustification,<A NAME="auto1134"></A><LI>quitting the application,<A NAME="auto1135"></A><LI>and on and on.</UL><A NAME="auto1136"></A><P>Lexi provides different user interfaces for these operations.But we don't want to associate a particular user operation with aparticular user interface, because we may want multiple userinterfaces to the same operation (you can turn the page using either apage button or a menu operation, for example).  We may also want tochange the interface in the future.</P><A NAME="auto1137"></A><P>Furthermore, these operations are implemented in many differentclasses.  We as implementors want to access their functionalitywithout creating a lot of dependencies between implementation and userinterface classes.  Otherwise we'll end up with a tightly coupledimplementation, which will be harder to understand, extend, andmaintain.</P><A NAME="undo-redo"></A><P>To further complicate matters, we want Lexi to support undo andredo<A NAME="fn8"></A><A HREF="#footnote8"><SUP>8</SUP></A>ofmost <EM>but not all</EM> its functionality.  Specifically, we want to beable to undo document-modifying operations like delete, with which auser can destroy lots of data inadvertently.  But we shouldn't try toundo an operation like saving a drawing or quitting the application.These operations should have no effect on the undo process.  We alsodon't want an arbitrary limit on the number of levels of undo andredo.</P><A NAME="auto1138"></A><P>It's clear that support for user operations permeates the application.The challenge is to come up with a simple and extensible mechanismthat satisfies all of these needs.</P><A NAME="encap-request"></A><H3>Encapsulating a Request</H3><A NAME="auto1139"></A><P>From our perspective as designers, a pull-down menu is just anotherkind of glyph that contains other glyphs.  What distinguishespull-down menus from other glyphs that have children is that mostglyphs in menus do some work in response to an up-click.</P><A NAME="auto1140"></A><P>Let's assume that these work-performing glyphs are instances of aGlyph subclass called <STRONG>MenuItem</STRONG> and that they do their work inresponse to a request from a client.<A NAME="fn9"></A><A HREF="#footnote9"><SUP>9</SUP></A>Carrying out therequest might involve an operation on one object, or many operationson many objects, or something in between.</P><A NAME="auto1141"></A><P>We could define a subclass of MenuItem for every user operation andthen hard-code each subclass to carry out the request.  But that's notreally right; we don't need a subclass of MenuItem for each requestany more than we need a subclass for each text string in a pull-downmenu.  Moreover, this approach couples the request to a particularuser interface, making it hard to fulfill the request through adifferent user interface.</P><A NAME="auto1142"></A><P>To illustrate, suppose you could advance to the last page in thedocument both through a MenuItem in a pull-down menu <EM>and</EM> bypressing a page icon at the bottom of Lexi's interface (which mightbe more convenient for short documents).  If we associate the requestwith a MenuItem through inheritance, then we must do the same for thepage icon and any other kind of widget that might issue such arequest.  That can give rise to a number of classes approaching theproduct of the number of widget types and the number of requests.</P><A NAME="auto1143"></A><P>What's missing is a mechanism that lets us parameterize menu items bythe request they should fulfill.  That way we avoid a proliferation ofsubclasses and allow for greater flexibility at run-time.  We couldparameterize MenuItem with a function to call, but that's not a completesolution for at least three reasons:</P><OL><A NAME="auto1144"></A><LI>It doesn't address the undo/redo problem.</LI><A NAME="auto1145"></A><P></P><A NAME="auto1146"></A><LI>It's hard to associate state with a function.  For example, afunction that changes the font needs to know <EM>which</EM> font.</LI><A NAME="auto1147"></A><P></P><A NAME="auto1148"></A><LI>Functions are hard to extend, and it's hard to reuse parts of them.</LI></OL><A NAME="auto1149"></A><P>These reasons suggest that we should parameterize M

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -