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

📄 chap2.htm

📁 设计模式英文版 作者:Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides 四人帮的书。 学设计模式的必读的书籍!经典中的经典
💻 HTM
📖 第 1 页 / 共 5 页
字号:
</PRE>

<A NAME="auto1112"></A>
<P>The default implementation of <CODE>DrawRect</CODE> uses the abstract
operation 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 the
WindowImp with which the Window is configured.  The window
implementation is defined by the instance of the WindowImp subclass
that <CODE>_imp</CODE> points to.  For an XWindowImp (that is, a
WindowImp subclass for the X Window System), the
<CODE>DeviceRect</CODE>'s implementation might look like

<A NAME="auto1114"></A>
<PRE>
    void XWindowImp::DeviceRect (
        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 values
from those supplied.  First it ascertains the lower left corner
(since (<CODE>x0</CODE>, <CODE>y0</CODE>) might be any one
of the rectangle's four corners) and then calculates the width and
height.</P>

<A NAME="present-manage"></A>
<P>PMWindowImp (a subclass of WindowImp for Presentation Manager) would
define <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 an
operation for drawing rectangles explicitly as X does. Instead, PM has a
more general interface for specifying vertices of multisegment shapes
(called a <STRONG>path</STRONG>) and for outlining or filling the area they
enclose.</P>

<A NAME="auto1117"></A>
<P>PM's implementation of <CODE>DeviceRect</CODE> is obviously quite
different from X's, but that doesn't matter.  WindowImp hides
variations in window system interfaces behind a potentially large but
stable interface.  That lets Window subclass writers focus on the window
abstraction and not on window system details.  It also lets us add
support 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 with
the proper WindowImp subclass in the first place.  Stated another way,
when does <CODE>_imp</CODE> get initialized, and who knows what window
system (and consequently which WindowImp subclass) is in use?  The
window will need some kind of WindowImp before it can do anything
interesting.</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&nbsp;(87)</A> pattern.  We can define
an abstract factory class WindowSystemFactory that provides an
interface for creating different kinds of window system-dependent
implementation 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 window
system:</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 of
a 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 same
way.</P>

<H3>Bridge Pattern</H3>

<A NAME="auto1125"></A>
<P>The WindowImp class defines an interface to common window system
facilities, but its design is driven by different constraints than
Window's interface.  Application programmers won't deal with
WindowImp's interface directly; they only deal with Window objects.
So WindowImp's interface needn't match the application programmer's
view of the world, as was our concern in the design of the Window
class hierarchy and interface.  WindowImp's interface can more closely
reflect what window systems actually provide, warts and all. It can be
biased toward either an intersection or a union of functionality
approach, whichever suits the target window systems best.</P>

<A NAME="auto1126"></A>
<P>The important thing to realize is that Window's interface caters to
the applications programmer, while WindowImp caters to window systems.
Separating windowing functionality into Window and WindowImp
hierarchies lets us implement and specialize these interfaces
independently. Objects from these hierarchies cooperate to let
Lexi 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&nbsp;(151)</A> pattern. The intent behind Bridge is to allow
separate class hierarchies to work together even as they evolve
independently. Our design criteria led us to create two separate class
hierarchies, one that supports the logical notion of windows, and
another for capturing different implementations of windows. The Bridge
pattern lets us maintain and enhance our logical windowing
abstractions without touching window system-dependent code, and vice
versa.</P>

<A NAME="sec2-7"></A>
<H2><A HREF="#sec2-8"><IMG SRC="gifsb/down3.gif" BORDER=0></A>
User Operations</H2>

<A NAME="auto1128"></A>
<P>Some of Lexi's functionality is available through the document's
WYSIWYG representation.  You enter and delete text, move the insertion
point, and select ranges of text by pointing, clicking, and typing
directly in the document.  Other functionality is accessed indirectly
through user operations in Lexi's pull-down menus, buttons, and
keyboard 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 and
justification,

<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 a
particular user interface, because we may want multiple user
interfaces to the same operation (you can turn the page using either a
page button or a menu operation, for example).  We may also want to
change the interface in the future.</P>

<A NAME="auto1137"></A>
<P>Furthermore, these operations are implemented in many different
classes.  We as implementors want to access their functionality
without creating a lot of dependencies between implementation and user
interface classes.  Otherwise we'll end up with a tightly coupled
implementation, which will be harder to understand, extend, and
maintain.</P>

<A NAME="undo-redo"></A>
<P>To further complicate matters, we want Lexi to support undo and
redo<A NAME="fn8"></A><A HREF="#footnote8"><SUP>8</SUP></A>
of
most <EM>but not all</EM> its functionality.  Specifically, we want to be
able to undo document-modifying operations like delete, with which a
user can destroy lots of data inadvertently.  But we shouldn't try to
undo an operation like saving a drawing or quitting the application.
These operations should have no effect on the undo process.  We also
don't want an arbitrary limit on the number of levels of undo and
redo.</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 mechanism
that 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 another
kind of glyph that contains other glyphs.  What distinguishes
pull-down menus from other glyphs that have children is that most
glyphs 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 a
Glyph subclass called <STRONG>MenuItem</STRONG> and that they do their work in
response to a request from a client.<A NAME="fn9"></A><A HREF="#footnote9"><SUP>9</SUP></A>
Carrying out the
request might involve an operation on one object, or many operations
on many objects, or something in between.</P>

<A NAME="auto1141"></A>
<P>We could define a subclass of MenuItem for every user operation and
then hard-code each subclass to carry out the request.  But that's not
really right; we don't need a subclass of MenuItem for each request
any more than we need a subclass for each text string in a pull-down
menu.  Moreover, this approach couples the request to a particular
user interface, making it hard to fulfill the request through a
different user interface.</P>

<A NAME="auto1142"></A>
<P>To illustrate, suppose you could advance to the last page in the
document both through a MenuItem in a pull-down menu <EM>and</EM> by
pressing a page icon at the bottom of Lexi's interface (which mi

⌨️ 快捷键说明

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