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

📄 pat3c.htm

📁 四人帮《设计模式》一书英文版本
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<A NAME="auto1047"></A>
<P>Overriding a parameterized factory method lets you easily and
selectively extend or change the products that a Creator produces. You
can introduce new identifiers for new kinds of products, or you can
associate existing identifiers with different products.</P>

<A NAME="auto1048"></A>
<P>For example, a subclass <CODE>MyCreator</CODE> could swap MyProduct and
YourProduct and support a new <CODE>TheirProduct</CODE> subclass:</P>

<A NAME="auto1049"></A>
<PRE>
    Product* MyCreator::Create (ProductId id) {
        if (id == YOURS)  return new MyProduct;
        if (id == MINE)   return new YourProduct;
            // N.B.: switched YOURS and MINE
    
        if (id == THEIRS) return new TheirProduct;
    
        return Creator::Create(id); // called if all others fail
    }
</PRE>

<A NAME="auto1051"></A>
<P>Notice that the last thing this operation does is call
<CODE>Create</CODE> on the parent class.  That's because
<CODE>MyCreator::Create</CODE> handles only <CODE>YOURS</CODE>,
<CODE>MINE</CODE>, and <CODE>THEIRS</CODE> differently than the parent
class.  It isn't interested in other classes.  Hence
<CODE>MyCreator</CODE> <EM>extends</EM> the kinds of products created, and it
defers responsibility for creating all but a few products to its
parent.</LI>

<A NAME="auto1052"></A>
<P></P>

<A NAME="fact-lang-var"></A>
<LI><EM>Language-specific variants and issues.</EM>
Different languages lend themselves to other interesting variations
and caveats.

<A NAME="auto1053"></A>
<P>Smalltalk programs often use a method that returns the class of the
object to be instantiated.  A Creator factory method can use this
value to create a product, and a ConcreteCreator may store or even
compute this value.  The result is an even later binding for the type
of ConcreteProduct to be instantiated.</P>

<A NAME="auto1054"></A>
<P>A Smalltalk version of the Document example can define a
<CODE>documentClass</CODE> method on <CODE>Application</CODE>.
The <CODE>documentClass</CODE> method returns the proper
<CODE>Document</CODE> class for instantiating documents.  The
implementation of
<CODE>documentClass</CODE> in <CODE>MyApplication</CODE> returns the
<CODE>MyDocument</CODE> class.  Thus in class
<CODE>Application</CODE> we have</P>

<A NAME="auto1055"></A>
<PRE>
    clientMethod
        document := self documentClass new.
    
    documentClass
        self subclassResponsibility
</PRE>

<A NAME="auto1056"></A>
<P>In class <CODE>MyApplication</CODE> we have</P>

<A NAME="auto1057"></A>
<PRE>
    documentClass
        ^ MyDocument
</PRE>

<A NAME="auto1058"></A>
<P>which returns the class <CODE>MyDocument</CODE> to be instantiated to
<CODE>Application</CODE>.</P>

<A NAME="auto1059"></A>
<P>An even more flexible approach akin to parameterized factory methods
is to store the class to be created as a class variable of
<CODE>Application</CODE>.  That way you don't have to subclass
<CODE>Application</CODE> to vary the product.</P>

<A NAME="auto1060"></A>
<P>Factory methods in C++ are always virtual functions and are often pure
virtual.  Just be careful not to call factory methods in the Creator's
constructor&#151;the factory method in the ConcreteCreator won't be
available yet.</P>

<A NAME="lazy-init"></A>
<P>You can avoid this by being careful to access products solely through
accessor operations that create the product on demand.  Instead of
creating the concrete product in the constructor, the constructor
merely initializes it to 0.  The accessor returns the product.  But
first it checks to make sure the product exists, and if it doesn't,
the accessor creates it.  This technique is sometimes called
<STRONG>lazy initialization</STRONG>.  The following code shows a typical
implementation:</P>

<A NAME="auto1061"></A>
<PRE>
    class Creator {
    public:
        Product* GetProduct();
    protected:
        virtual Product* CreateProduct();
    private:
        Product* _product;
    };
    
    Product* Creator::GetProduct () {
        if (_product == 0) {
            _product = CreateProduct();
        }
        return _product;
    }
</PRE>

<A NAME="auto1062"></A>
<P></P>

<A NAME="template-impl-factmeth"></A>
<LI><EM>Using templates to avoid subclassing.</EM>
As we've mentioned, another potential problem with factory methods is
that they might force you to subclass just to create the appropriate
Product objects.  Another way to get around this in C++ is to provide
a template subclass of Creator that's parameterized by the Product
class:

<A NAME="auto1063"></A>
<PRE>
    class Creator {
    public:
        virtual Product* CreateProduct() = 0;
    };
    
    template &lt;class TheProduct>
    class StandardCreator: public Creator {
    public:
        virtual Product* CreateProduct();
    };
    
    template &lt;class TheProduct>
    Product* StandardCreator&lt;TheProduct>::CreateProduct () {
        return new TheProduct;
    }
</PRE>

<A NAME="auto1064"></A>
<P>With this template, the client supplies just the product class&#151;no
subclassing of Creator is required.

<A NAME="auto1065"></A>
<PRE>
    class MyProduct : public Product {
    public:
        MyProduct();
        // ...
    };
    
    StandardCreator&lt;MyProduct> myCreator;
</PRE>

</LI>

<A NAME="auto1066"></A>
<P></P>

<A NAME="macapp-facmeth"></A>
<LI><EM>Naming conventions.</EM>
It's good practice to use naming conventions that make it clear you're
using factory methods.  For example, the MacApp Macintosh application
framework [<A HREF="bibfs.htm#macapp" tppabs="http://ultra/development/DesignPatterns/hires/bibfs.htm#macapp" TARGET="_mainDisplayFrame">App89</A>] always declares the abstract operation that
defines the factory method as <CODE>Class* DoMakeClass()</CODE>, where
<CODE>Class</CODE> is the Product class.</LI>

</OL>

<A NAME="samplecode"><A>
<H2><A HREF="#knownuses"><IMG SRC="down3.gif" tppabs="http://ultra/development/DesignPatterns/hires/gifsb/down3.gif" BORDER=0 ALT="next: 
Known Uses"></A> Sample Code</H2> 

<A NAME="auto1067"></A>
<P>The function <CODE>CreateMaze</CODE>
(<A HREF="chap3fs.htm#CreateMaze-def" tppabs="http://ultra/development/DesignPatterns/hires/chap3fs.htm#CreateMaze-def" TARGET="_mainDisplayFrame">page 84</A>) builds and returns a
maze. One problem with this function is that it hard-codes the
classes of maze, rooms, doors, and walls.  We'll introduce factory
methods to let subclasses choose these components.</P>

<A NAME="auto1068"></A>
<P>First we'll define factory methods in <CODE>MazeGame</CODE> for
creating the maze, room, wall, and door objects:</P>

<A NAME="auto1069"></A>
<PRE>
    class MazeGame {
    public:
        Maze* CreateMaze();
    
    // factory methods:
    
        virtual Maze* MakeMaze() const
            { return new Maze; }
        virtual Room* MakeRoom(int n) const
            { return new Room(n); }
        virtual Wall* MakeWall() const
            { return new Wall; }
        virtual Door* MakeDoor(Room* r1, Room* r2) const
            { return new Door(r1, r2); }
    };
</PRE>

<A NAME="auto1070"></A>
<P>Each factory method returns a maze component of a given type.
<CODE>MazeGame</CODE> provides default implementations that return the
simplest kinds of maze, rooms, walls, and doors.</P>

<A NAME="auto1071"></A>
<P>Now we can rewrite <CODE>CreateMaze</CODE> to use these factory methods:</P>

<A NAME="auto1072"></A>
<PRE>
    Maze* MazeGame::CreateMaze () {
        Maze* aMaze = MakeMaze();
    
        Room* r1 = MakeRoom(1);
        Room* r2 = MakeRoom(2);
        Door* theDoor = MakeDoor(r1, r2);
    
        aMaze->AddRoom(r1);
        aMaze->AddRoom(r2);
    
        r1->SetSide(North, MakeWall());
        r1->SetSide(East, theDoor);
        r1->SetSide(South, MakeWall());
        r1->SetSide(West, MakeWall());
    
        r2->SetSide(North, MakeWall());
        r2->SetSide(East, MakeWall());
        r2->SetSide(South, MakeWall());
        r2->SetSide(West, theDoor);
    
        return aMaze;
    }
</PRE>

<A NAME="auto1073"></A>
<P>Different games can subclass <CODE>MazeGame</CODE> to specialize parts of
the maze. <CODE>MazeGame</CODE> subclasses can redefine some or all of
the factory methods to specify variations in products.  For example, a
<CODE>BombedMazeGame</CODE> can redefine the <CODE>Room</CODE> and
<CODE>Wall</CODE> products to return the bombed varieties:</P>

<A NAME="auto1074"></A>
<PRE>
    class BombedMazeGame : public MazeGame {
    public:
        BombedMazeGame();
    
        virtual Wall* MakeWall() const
            { return new BombedWall; }
    
        virtual Room* MakeRoom(int n) const
            { return new RoomWithABomb(n); }
    };
</PRE>

<A NAME="auto1075"></A>
<P>An <CODE>EnchantedMazeGame</CODE> variant might be defined like this:</P>

<A NAME="auto1076"></A>
<PRE>
    class EnchantedMazeGame : public MazeGame {
    public:
        EnchantedMazeGame();
    
        virtual Room* MakeRoom(int n) const
            { return new EnchantedRoom(n, CastSpell()); }
    
        virtual Door* MakeDoor(Room* r1, Room* r2) const
            { return new DoorNeedingSpell(r1, r2); }
    protected:
        Spell* CastSpell() const;
    };
</PRE>

<A NAME="knownuses"><A>
<H2><A HREF="#relatedpatterns"><IMG SRC="down3.gif" tppabs="http://ultra/development/DesignPatterns/hires/gifsb/down3.gif" BORDER=0 
ALT="next: Related Patterns"></A> Known Uses</H2> 

<A NAME="auto1077"></A>
<P>Factory methods pervade toolkits and frameworks. The preceding
document example is a typical use in MacApp and ET++ [<A HREF="bibfs.htm#et++" tppabs="http://ultra/development/DesignPatterns/hires/bibfs.htm#et++" TARGET="_mainDisplayFrame">WGM88</A>].  The
manipulator example is from Unidraw.</P>

<A NAME="smalltalk-use-factmeth"></A>
<P>Class View in the Smalltalk-80 Model/View/Controller framework has a method
defaultController that creates a controller, and this might appear to
be a factory method [<A HREF="bibfs.htm#parcplace_smalltalk" tppabs="http://ultra/development/DesignPatterns/hires/bibfs.htm#parcplace_smalltalk" TARGET="_mainDisplayFrame">Par90</A>].  But subclasses of
View specify the class of their default controller by defining
defaultControllerClass, which returns the class from which
defaultController creates instances.  So defaultControllerClass is the
real factory method, that is, the method that subclasses should
override.</P>

<A NAME="auto1078"></A>
<P>A more esoteric example in Smalltalk-80 is the factory method
parserClass defined by Behavior (a superclass of all objects
representing classes).  This enables a class to use a customized
parser for its source code.  For example, a client can define a class
SQLParser to analyze the source code of a class with embedded SQL
statements.  The Behavior class implements parserClass to return the
standard Smalltalk Parser class. A class that includes embedded SQL
statements overrides this method (as a class method) and returns the
SQLParser class.</P>

<A NAME="orbix-use-factmeth"></A>
<P>The Orbix ORB system from IONA Technologies [<A HREF="bibfs.htm#orbix-orb" tppabs="http://ultra/development/DesignPatterns/hires/bibfs.htm#orbix-orb" TARGET="_mainDisplayFrame">ION94</A>] uses
Factory Method to generate an appropriate type of proxy (see
<A HREF="pat4gfs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat4gfs.htm" TARGET="_mainDisplayFrame">Proxy (207)</A>) when an object requests a reference to a
remote object.  Factory Method makes it easy to replace the
default proxy with one that uses client-side caching, for example.</P>

<A NAME="relatedpatterns"></A>
<H2><A HREF="#last"><IMG SRC="down3.gif" tppabs="http://ultra/development/DesignPatterns/hires/gifsb/down3.gif" BORDER=0 ALT="next: 
navigation"></A> Related Patterns</H2> 

<A NAME="auto1079"></A>
<P><A HREF="pat3afs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat3afs.htm" TARGET="_mainDisplayFrame">Abstract Factory (87)</A> is
often implemented with factory methods.  The Motivation example in
the Abstract Factory pattern illustrates Factory Method as well.</P>

<A NAME="templ-call-factmeth"></A>
<P>Factory methods are usually called within
<A HREF="pat5jfs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat5jfs.htm" TARGET="_mainDisplayFrame">Template
Methods (325)</A>.  In the document example above,
NewDocument is a template method.</P>

<A NAME="proto-vs-factmeth"></A>
<P><A HREF="pat3dfs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat3dfs.htm" TARGET="_mainDisplayFrame">Prototypes (117)</A>
don't require subclassing Creator.  However, they often require an
Initialize operation on the Product class.  Creator uses Initialize
to initialize the object.  Factory Method doesn't require such an
operation.</P>

<A NAME="last"></A>
<P><A HREF="#intent"><IMG SRC="up3.gif" tppabs="http://ultra/development/DesignPatterns/hires/gifsb/up3.gif" BORDER=0></A><BR>
<A HREF="pat3dfs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat3dfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="rightar3.gif" tppabs="http://ultra/development/DesignPatterns/hires/gifsb/rightar3.gif"
	ALIGN=TOP BORDER=0></A> <A HREF="pat3dfs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat3dfs.htm"
	TARGET="_mainDisplayFrame">Prototype</A><BR>
<A HREF="pat3bfs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat3bfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="leftarr3.gif" tppabs="http://ultra/development/DesignPatterns/hires/gifsb/leftarr3.gif"
	ALIGN=TOP BORDER=0></A> <A HREF="pat3bfs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat3bfs.htm"
	TARGET="_mainDisplayFrame">Builder</A>
</P>

</BODY>

</HTML>

<A NAME="auto1080"></A>
<P>


⌨️ 快捷键说明

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