📄 pat3c.htm
字号:
associate existing identifiers with different products.</P><A NAME="auto1048"></A><P>For example, a subclass <CODE>MyCreator</CODE> could swap MyProduct andYourProduct 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 parentclass. It isn't interested in other classes. Hence<CODE>MyCreator</CODE> <EM>extends</EM> the kinds of products created, and itdefers responsibility for creating all but a few products to itsparent.</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 variationsand caveats.<A NAME="auto1053"></A><P>Smalltalk programs often use a method that returns the class of theobject to be instantiated. A Creator factory method can use thisvalue to create a product, and a ConcreteCreator may store or evencompute this value. The result is an even later binding for the typeof 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. Theimplementation 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 methodsis 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 purevirtual. Just be careful not to call factory methods in the Creator'sconstructor—the factory method in the ConcreteCreator won't beavailable yet.</P><A NAME="lazy-init"></A><P>You can avoid this by being careful to access products solely throughaccessor operations that create the product on demand. Instead ofcreating the concrete product in the constructor, the constructormerely initializes it to 0. The accessor returns the product. Butfirst 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 typicalimplementation:</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 isthat they might force you to subclass just to create the appropriateProduct objects. Another way to get around this in C++ is to providea template subclass of Creator that's parameterized by the Productclass:<A NAME="auto1063"></A><PRE> class Creator { public: virtual Product* CreateProduct() = 0; }; template <class TheProduct> class StandardCreator: public Creator { public: virtual Product* CreateProduct(); }; template <class TheProduct> Product* StandardCreator<TheProduct>::CreateProduct () { return new TheProduct; }</PRE><A NAME="auto1064"></A><P>With this template, the client supplies just the product class—nosubclassing of Creator is required.<A NAME="auto1065"></A><PRE> class MyProduct : public Product { public: MyProduct(); // ... }; StandardCreator<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'reusing factory methods. For example, the MacApp Macintosh applicationframework [<A HREF="bibfs.htm#macapp" TARGET="_mainDisplayFrame">App89</A>] always declares the abstract operation thatdefines 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="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" TARGET="_mainDisplayFrame">page 84</A>) builds and returns amaze. One problem with this function is that it hard-codes theclasses of maze, rooms, doors, and walls. We'll introduce factorymethods to let subclasses choose these components.</P><A NAME="auto1068"></A><P>First we'll define factory methods in <CODE>MazeGame</CODE> forcreating 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 thesimplest 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 ofthe maze. <CODE>MazeGame</CODE> subclasses can redefine some or all ofthe 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="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 precedingdocument example is a typical use in MacApp and ET++ [<A HREF="bibfs.htm#et++" TARGET="_mainDisplayFrame">WGM88</A>]. Themanipulator example is from Unidraw.</P><A NAME="smalltalk-use-factmeth"></A><P>Class View in the Smalltalk-80 Model/View/Controller framework has a methoddefaultController that creates a controller, and this might appear tobe a factory method [<A HREF="bibfs.htm#parcplace_smalltalk" TARGET="_mainDisplayFrame">Par90</A>]. But subclasses ofView specify the class of their default controller by definingdefaultControllerClass, which returns the class from whichdefaultController creates instances. So defaultControllerClass is thereal factory method, that is, the method that subclasses shouldoverride.</P><A NAME="auto1078"></A><P>A more esoteric example in Smalltalk-80 is the factory methodparserClass defined by Behavior (a superclass of all objectsrepresenting classes). This enables a class to use a customizedparser for its source code. For example, a client can define a classSQLParser to analyze the source code of a class with embedded SQLstatements. The Behavior class implements parserClass to return thestandard Smalltalk Parser class. A class that includes embedded SQLstatements overrides this method (as a class method) and returns theSQLParser class.</P><A NAME="orbix-use-factmeth"></A><P>The Orbix ORB system from IONA Technologies [<A HREF="bibfs.htm#orbix-orb" TARGET="_mainDisplayFrame">ION94</A>] usesFactory Method to generate an appropriate type of proxy (see<A HREF="pat4gfs.htm" TARGET="_mainDisplayFrame">Proxy (207)</A>) when an object requests a reference to aremote object. Factory Method makes it easy to replace thedefault proxy with one that uses client-side caching, for example.</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="auto1079"></A><P><A HREF="pat3afs.htm" TARGET="_mainDisplayFrame">Abstract Factory (87)</A> isoften implemented with factory methods. The Motivation example inthe 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" TARGET="_mainDisplayFrame">TemplateMethods (325)</A>. In the document example above,NewDocument is a template method.</P><A NAME="proto-vs-factmeth"></A><P><A HREF="pat3dfs.htm" TARGET="_mainDisplayFrame">Prototypes (117)</A>don't require subclassing Creator. However, they often require anInitialize operation on the Product class. Creator uses Initializeto initialize the object. Factory Method doesn't require such anoperation.</P><A NAME="last"></A><P><A HREF="#intent"><IMG SRC="gifsb/up3.gif" BORDER=0></A><BR><A HREF="pat3dfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/rightar3.gif" ALIGN=TOP BORDER=0></A> <A HREF="pat3dfs.htm" TARGET="_mainDisplayFrame">Prototype</A><BR><A HREF="pat3bfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/leftarr3.gif" ALIGN=TOP BORDER=0></A> <A HREF="pat3bfs.htm" TARGET="_mainDisplayFrame">Builder</A></P></BODY></HTML><A NAME="auto1080"></A><P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -