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

📄 pat4g.htm

📁 Design Pattern 设计模式
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<A NAME="auto1051"></A><PRE>    ImagePtr image = ImagePtr("anImageFileName");    image->Draw(Point(50, 100));          // (image.operator->())->Draw(Point(50, 100))</PRE><A NAME="auto1052"></A><P>Notice how the <CODE>image</CODE> proxy acts like a pointer, but it's notdeclared to be a pointer to an <CODE>Image</CODE>.  That means you can'tuse it exactly like a real pointer to an<CODE>Image</CODE>.  Hence clients must treat <CODE>Image</CODE> and<CODE>ImagePtr</CODE> objects differently in this approach.</P><A NAME="auto1053"></A><P>Overloading the member access operator isn't a good solution for everykind of proxy.  Some proxies need to know precisely <EM>which</EM>operation is called, and overloading the member access operatordoesn't work in those cases.</P><A NAME="auto1054"></A><P>Consider the virtual proxy example in the Motivation.  The imageshould be loaded at a specific time&#151;namely when the Draw operationis called&#151;and not whenever the image is referenced.  Overloading theaccess operator doesn't allow this distinction.  In that case we mustmanually implement each proxy operation that forwards the request tothe subject.</P><A NAME="auto1055"></A><P>These operations are usually very similar to each other, as the SampleCode demonstrates.  Typically all operations verify that the requestis legal, that the original object exists, etc., before forwarding therequest to the subject.  It's tedious to write this code again andagain.  So it's common to use a preprocessor to generate itautomatically.</P></LI><A NAME="using-dnu"></A><LI><EM>Using <CODE>doesNotUnderstand</CODE> in Smalltalk.</EM>Smalltalk provides a hook that you can use tosupport automatic forwarding of requests.  Smalltalk calls<CODE>doesNotUnderstand: aMessage</CODE> when a client sends a message toa receiver that has no corresponding method.  The Proxy class canredefine <CODE>doesNotUnderstand</CODE> so that the message is forwardedto its subject.<A NAME="auto1056"></A><P>To ensure that a request is forwarded to the subject and not justabsorbed by the proxy silently, you can define a Proxy class thatdoesn't understand <EM>any</EM> messages.  Smalltalk lets you do this bydefining Proxy as a class with nosuperclass.<A NAME="fn6"></A><SUP><A HREF="#footnote6">6</A></SUP></P><A NAME="auto1057"></A><P>The main disadvantage of <CODE>doesNotUnderstand:</CODE> is that mostSmalltalk systems have a few special messages that are handleddirectly by the virtual machine, and these do not cause the usualmethod look-up.  The only one that's usually implemented in Object (andso can affect proxies) is the identity operation <CODE>==</CODE>.</P><A NAME="auto1058"></A><P>If you're going to use <CODE>doesNotUnderstand:</CODE> to implementProxy, then you must design around this problem.  You can't expectidentity on proxies to mean identity on their real subjects.  An addeddisadvantage is that <CODE>doesNotUnderstand:</CODE> was developed forerror handling, not for building proxies, and so it's generally notvery fast.</P></LI><A NAME="auto1059"></A><P></P><A NAME="auto1060"></A><LI><EM>Proxy doesn't always have to know the type of real subject.</EM>If a Proxy class can deal with its subject solely through an abstractinterface, then there's no need to make a Proxy class for eachRealSubject class; the proxy can deal with all RealSubject classesuniformly.  But if Proxies are going to instantiate RealSubjects (suchas in a virtual proxy), then they have to know the concrete class.</LI></OL><A NAME="auto1061"></A><P>Another implementation issue involves how to refer to the subjectbefore it's instantiated.  Some proxies have to refer to their subjectwhether it's on disk or in memory.  That means they must use some formof address space-independent object identifiers.  We used a file namefor this purpose in the Motivation.</P><A NAME="samplecode"></A><H2><A HREF="#knownuses"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Known Uses"></A> Sample Code</H2> <A NAME="auto1062"></A><P>The following code implements two kinds of proxy: the virtual proxydescribed in the Motivation section, and a proxy implemented with<CODE>doesNotUnderstand:</CODE>.<A NAME="fn7"></A><SUP><A HREF="#footnote7">7</A></SUP></P><OL><A NAME="auto1063"></A><LI><EM>A virtual proxy.</EM>The <CODE>Graphic</CODE> class defines the interface for graphicalobjects:<A NAME="auto1064"></A><PRE>    class Graphic {    public:        virtual ~Graphic();            virtual void Draw(const Point&amp; at) = 0;        virtual void HandleMouse(Event&amp; event) = 0;            virtual const Point&amp; GetExtent() = 0;            virtual void Load(istream&amp; from) = 0;        virtual void Save(ostream&amp; to) = 0;    protected:        Graphic();    };</PRE><A NAME="auto1065"></A><P>The <CODE>Image</CODE> class implements the <CODE>Graphic</CODE> interfaceto display image files. <CODE>Image</CODE> overrides<CODE>HandleMouse</CODE> to let users resize the image interactively.</P><A NAME="auto1066"></A><PRE>    class Image : public Graphic {    public:        Image(const char* file);  // loads image from a file        virtual ~Image();            virtual void Draw(const Point&amp; at);        virtual void HandleMouse(Event&amp; event);            virtual const Point&amp; GetExtent();            virtual void Load(istream&amp; from);        virtual void Save(ostream&amp; to);    private:        // ...    };</PRE><A NAME="auto1067"></A><P><CODE>ImageProxy</CODE> has the same interface as <CODE>Image</CODE>:</P><A NAME="auto1068"></A><PRE>    class ImageProxy : public Graphic {    public:        ImageProxy(const char* imageFile);        virtual ~ImageProxy();            virtual void Draw(const Point&amp; at);        virtual void HandleMouse(Event&amp; event);            virtual const Point&amp; GetExtent();            virtual void Load(istream&amp; from);        virtual void Save(ostream&amp; to);    protected:        Image* GetImage();    private:        Image* _image;        Point _extent;        char* _fileName;    };</PRE><A NAME="auto1069"></A><P>The constructor saves a local copy of the name of the file that storesthe image, and it initializes <CODE>_extent</CODE> and<CODE>_image</CODE>:</P><A NAME="auto1070"></A><PRE>    ImageProxy::ImageProxy (const char* fileName)  {        _fileName = strdup(fileName);        _extent = Point::Zero;  // don't know extent yet        _image = 0;    }        Image* ImageProxy::GetImage() {        if (_image == 0) {            _image = new Image(_fileName);        }        return _image;    }</PRE><A NAME="auto1071"></A><P>The implementation of <CODE>GetExtent</CODE> returns the cached extent ifpossible; otherwise the image is loaded from the file. <CODE>Draw</CODE>loads the image, and <CODE>HandleMouse</CODE> forwards the event to thereal image.</P><A NAME="auto1072"></A><PRE>    const Point&amp; ImageProxy::GetExtent () {        if (_extent == Point::Zero) {            _extent = GetImage()->GetExtent();        }        return _extent;    }        void ImageProxy::Draw (const Point&amp; at) {        GetImage()->Draw(at);    }        void ImageProxy::HandleMouse (Event&amp; event) {        GetImage()->HandleMouse(event);    }</PRE><A NAME="auto1073"></A><P>The <CODE>Save</CODE> operation saves the cached image extent and theimage file name to a stream. <CODE>Load</CODE> retrieves this informationand initializes the corresponding members.</P><A NAME="auto1074"></A><PRE>    void ImageProxy::Save (ostream&amp; to) {        to &lt;&lt; _extent &lt;&lt; _fileName;    }        void ImageProxy::Load (istream&amp; from) {        from >> _extent >> _fileName;    }</PRE><A NAME="auto1075"></A><P>Finally, suppose we have a class <CODE>TextDocument</CODE> that can contain<CODE>Graphic</CODE> objects:</P><A NAME="auto1076"></A><PRE>    class TextDocument {    public:        TextDocument();            void Insert(Graphic*);        // ...    };</PRE><A NAME="auto1077"></A><P>We can insert an <CODE>ImageProxy</CODE> into a text document like this:</P><A NAME="auto1078"></A><PRE>    TextDocument* text = new TextDocument;    // ...    text->Insert(new ImageProxy("anImageFileName"));</PRE></LI><A NAME="proxyuse-dnu"></A><LI><EM>Proxies that use <CODE>doesNotUnderstand</CODE>.</EM> Youcan make generic proxies in Smalltalk by defining classes whosesuperclass is nil<A NAME="fn8"></A><SUP><A HREF="#footnote8">8</A></SUP>and defining the <CODE>doesNotUnderstand:</CODE> method to handlemessages.<A NAME="auto1079"></A><P>The following method assumes the proxy has a <CODE>realSubject</CODE>method that returns its real subject.  In the case of ImageProxy, thismethod would check to see if the the Image had been created, create itif necessary, and finally return it.  It uses<CODE>perform:withArguments:</CODE> to perform the message being trappedon the real subject.</P><A NAME="auto1080"></A><PRE>    doesNotUnderstand: aMessage        ^ self realSubject            perform: aMessage selector            withArguments: aMessage arguments</PRE><A NAME="auto1081"></A><P>The argument to <CODE>doesNotUnderstand:</CODE> is an instance of<CODE>Message</CODE> that represents the message not understood by theproxy.  So the proxy responds to all messages by making sure that thereal subject exists before forwarding the message to it.</P><A NAME="auto1082"></A><P>One of the advantages of <CODE>doesNotUnderstand:</CODE> is it canperform arbitrary processing.  For example, we could produce aprotection proxy by specifying a set <CODE>legalMessages</CODE> ofmessages to accept and then giving the proxy the following method:</P><A NAME="auto1083"></A><PRE>    doesNotUnderstand: aMessage        ^ (legalMessages includes: aMessage selector)            ifTrue: [self realSubject                perform: aMessage selector                withArguments: aMessage arguments]            ifFalse: [self error: 'Illegal operator']</PRE><A NAME="auto1084"></A><P>This method checks to see that a message is legal before forwarding itto the real subject. If it isn't legal, then it will send<CODE>error:</CODE> to the proxy,which will result in an infinite loop of errors unless the proxydefines <CODE>error:</CODE>.  Consequently, the definition of<CODE>error:</CODE> should be copied from class Object along withany methods it uses.</P></LI></OL><A NAME="knownuses"></A><H2><A HREF="#relatedpatterns"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Related Patterns"></A> Known Uses</H2> <A NAME="auto1085"></A><P>The virtual proxy example in the Motivation section is from the ET++text building block classes.</P><A NAME="auto1086"></A><P>NEXTSTEP [<A HREF="bibfs.htm#NeXT_AppKit" TARGET="_mainDisplayFrame">Add94</A>] uses proxies (instances of class NXProxy)as local representatives for objects that may be distributed.  Aserver creates proxies for remote objects when clients request them.On receiving a message, the proxy encodes it along with its argumentsand then forwards the encoded message to the remote subject.Similarly, the subject encodes any return results and sends them backto the NXProxy object.</P><A NAME="auto1087"></A><P>McCullough [<A HREF="bibfs.htm#mccullough_forwarding" TARGET="_mainDisplayFrame">McC87</A>] discusses using proxies inSmalltalk to access remote objects. Pascoe [<A HREF="bibfs.htm#pascoe_encapsulators" TARGET="_mainDisplayFrame">Pas86</A>]describes how to provide side-effects on method calls and accesscontrol with "Encapsulators."</P><A NAME="relatedpatterns"></A><H2><A HREF="#last"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: navigation"></A> Related Patterns</H2> <A NAME="auto1088"></A><P><A HREF="pat4afs.htm" TARGET="_mainDisplayFrame">Adapter (139)</A>: Anadapter provides a different interface to the object it adapts. Incontrast, a proxy provides the same interface as its subject.However, a proxy used for access protection might refuse to performan operation that the subject will perform, so its interface maybe effectively a subset of the subject's.</P><A NAME="dec-prox"></A><P><A HREF="pat4dfs.htm" TARGET="_mainDisplayFrame">Decorator (175)</A>:Although decorators can have similar implementations as proxies,decorators have a different purpose. A decorator adds one or moreresponsibilities to an object, whereas a proxy controls access toan object.</P><A NAME="auto1089"></A><P>Proxies vary in the degree to which they are implemented like adecorator.  A protection proxy might be implemented exactly like adecorator.  On the other hand, a remote proxy will not contain adirect reference to its real subject but only an indirect reference,such as "host ID and local address on host."  A virtual proxy willstart off with an indirect reference such as a file name but willeventually obtain and use a direct reference.</P><A NAME="last"></A><P><A HREF="#intent"><IMG SRC="gifsb/up3.gif" BORDER=0></A><BR><A HREF="disc4fs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/rightar3.gif"	ALIGN=TOP BORDER=0></A> <A HREF="disc4fs.htm"	TARGET="_mainDisplayFrame">Discussion of Structural Patterns</A><BR><A HREF="pat4ffs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/leftarr3.gif"	ALIGN=TOP BORDER=0></A> <A HREF="pat4ffs.htm"	TARGET="_mainDisplayFrame">Flyweight</A></P><HR><A NAME="footnote6"></A><P><SUP>6</SUP>The implementation of distributed objects in NEXTSTEP [<A HREF="bibfs.htm#NeXT_AppKit" TARGET="_mainDisplayFrame">Add94</A>](specifically, the class NXProxy) uses this technique.  Theimplementation redefines <CODE>forward</CODE>, the equivalent hookin NEXTSTEP.</P><A NAME="footnote7"></A><P><SUP>7</SUP><A HREF="pat5dfs.htm" TARGET="_mainDisplayFrame">Iterator (257)</A>describes another kind of proxy on<A HREF="pat5dfs.htm#clean-up_proxy_for_iterators" TARGET="_mainDisplayFrame">page 266</A>.</P><A NAME="footnote8"></A><P><SUP>8</SUP>Almost all classes ultimately have Objectas their superclass.  Hence this is the same as saying "defining aclass that doesn't have Object as its superclass."</P></BODY></HTML>

⌨️ 快捷键说明

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