📄 chap2-1.htm
字号:
<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 to
hide different window system implementations. WindowImp is an abstract
class for objects that encapsulate window system-dependent code. To make
Lexi work on a particular window system, we configure each window
object with an instance of a WindowImp subclass for that system. The
following diagram shows the relationship between the Window and WindowImp
hierarchies:</P>
<A NAME="55c"></A>
<P ALIGN=CENTER><IMG SRC="windo111-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/Pictures/windo111.gif"></P>
<A NAME="auto1109"></A>
<P>By hiding the implementations in WindowImp classes, we avoid polluting
the Window classes with window system dependencies, which keeps the
Window class hierarchy comparatively small and stable. Meanwhile we
can easily extend the implementation hierarchy to support new window
systems.</P>
<H3>WindowImp Subclasses</H3>
<A NAME="auto1110"></A>
<P>Subclasses of WindowImp convert requests into window system-specific
operations. 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 on
the 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 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, &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-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat3afs.htm" TARGET="_mainDisplayFrame">Abstract Factory (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-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat4bfs.htm" TARGET="_mainDisplayFrame">Bridge (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="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/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'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, w
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -