📄 pat3b.htm
字号:
<A NAME="auto1052"></A><PRE> class MazeBuilder { public: virtual void BuildMaze() { } virtual void BuildRoom(int room) { } virtual void BuildDoor(int roomFrom, int roomTo) { } virtual Maze* GetMaze() { return 0; } protected: MazeBuilder(); };</PRE><A NAME="auto1053"></A><P>This interface can create three things: (1) the maze, (2) rooms with aparticular room number, and (3) doors between numbered rooms. The<CODE>GetMaze</CODE> operation returns the maze to the client.Subclasses of <CODE>MazeBuilder</CODE> will override this operation toreturn the maze that they build.</P><A NAME="auto1054"></A><P>All the maze-building operations of <CODE>MazeBuilder</CODE> do nothingby default. They're not declared pure virtual to let derived classesoverride only those methods in which they're interested.</P><A NAME="auto1055"></A><P>Given the <CODE>MazeBuilder</CODE> interface, we can change the<CODE>CreateMaze</CODE> member function to take this builder as aparameter.</P><A NAME="auto1056"></A><PRE> Maze* MazeGame::CreateMaze (MazeBuilder& builder) { builder.BuildMaze(); builder.BuildRoom(1); builder.BuildRoom(2); builder.BuildDoor(1, 2); return builder.GetMaze(); }</PRE><A NAME="auto1057"></A><P>Compare this version of <CODE>CreateMaze</CODE> with the original.Notice how the builder hides the internal representation of theMaze—that is, the classes that define rooms, doors, and walls—andhow these parts are assembled to complete the final maze. Someonemight guess that there are classes for representing rooms and doors,but there is no hint of one for walls. This makes it easier to changethe way a maze is represented, since none of the clients of<CODE>MazeBuilder</CODE> has to be changed.</P><A NAME="auto1058"></A><P>Like the other creational patterns, the Builder pattern encapsulateshow objects get created, in this case through the interface defined by<CODE>MazeBuilder</CODE>. That means we can reuse <CODE>MazeBuilder</CODE>to build different kinds of mazes. The <CODE>CreateComplexMaze</CODE>operation gives an example:</P><A NAME="auto1059"></A><PRE> Maze* MazeGame::CreateComplexMaze (MazeBuilder& builder) { builder.BuildRoom(1); // ... builder.BuildRoom(1001); return builder.GetMaze(); }</PRE><A NAME="auto1060"></A><P>Note that <CODE>MazeBuilder</CODE> does not create mazes itself; itsmain purpose is just to define an interface for creating mazes. Itdefines empty implementations primarily for convenience. Subclasses of<CODE>MazeBuilder</CODE> do the actual work.</P><A NAME="standardmazebuilder"></A><P>The subclass <CODE>StandardMazeBuilder</CODE> is an implementation thatbuilds simple mazes. It keeps track of the maze it's building in thevariable <CODE>_currentMaze</CODE>.</P><A NAME="auto1061"></A><PRE> class StandardMazeBuilder : public MazeBuilder { public: StandardMazeBuilder(); virtual void BuildMaze(); virtual void BuildRoom(int); virtual void BuildDoor(int, int); virtual Maze* GetMaze(); private: Direction CommonWall(Room*, Room*); Maze* _currentMaze; };</PRE><A NAME="auto1062"></A><P><CODE>CommonWall</CODE> is a utility operation that determinesthe direction of the common wall between two rooms.</P><A NAME="auto1063"></A><P>The <CODE>StandardMazeBuilder</CODE> constructor simply initializes<CODE>_currentMaze</CODE>.</P><A NAME="auto1064"></A><PRE> StandardMazeBuilder::StandardMazeBuilder () { _currentMaze = 0; }</PRE><A NAME="auto1065"></A><P><CODE>BuildMaze</CODE> instantiates a <CODE>Maze</CODE> thatother operations will assemble and eventually return to the client(with <CODE>GetMaze</CODE>).</P><A NAME="auto1066"></A><PRE> void StandardMazeBuilder::BuildMaze () { _currentMaze = new Maze; } Maze* StandardMazeBuilder::GetMaze () { return _currentMaze; }</PRE><A NAME="auto1067"></A><P>The <CODE>BuildRoom</CODE> operation creates a room and builds thewalls around it:</P><A NAME="auto1068"></A><PRE> void StandardMazeBuilder::BuildRoom (int n) { if (!_currentMaze->RoomNo(n)) { Room* room = new Room(n); _currentMaze->AddRoom(room); room->SetSide(North, new Wall); room->SetSide(South, new Wall); room->SetSide(East, new Wall); room->SetSide(West, new Wall); } }</PRE><A NAME="auto1069"></A><P>To build a door between two rooms, <CODE>StandardMazeBuilder</CODE> looksup both rooms in the maze and finds their adjoining wall:</P><A NAME="auto1070"></A><PRE> void StandardMazeBuilder::BuildDoor (int n1, int n2) { Room* r1 = _currentMaze->RoomNo(n1); Room* r2 = _currentMaze->RoomNo(n2); Door* d = new Door(r1, r2); r1->SetSide(CommonWall(r1,r2), d); r2->SetSide(CommonWall(r2,r1), d); }</PRE><A NAME="auto1071"></A><P>Clients can now use <CODE>CreateMaze</CODE> in conjunction with<CODE>StandardMazeBuilder</CODE> to create a maze:</P><A NAME="auto1072"></A><PRE> Maze* maze; MazeGame game; StandardMazeBuilder builder; game.CreateMaze(builder); maze = builder.GetMaze();</PRE><A NAME="auto1073"></A><P>We could have put all the <CODE>StandardMazeBuilder</CODE> operations in<CODE>Maze</CODE> and let each <CODE>Maze</CODE> build itself. But making<CODE>Maze</CODE> smaller makes it easier to understand and modify, and<CODE>StandardMazeBuilder</CODE> is easy to separate from <CODE>Maze</CODE>.Most importantly, separating the two lets you have a variety of<CODE>MazeBuilders</CODE>, each using different classes for rooms, walls,and doors.</P><A NAME="auto1074"></A><P>A more exotic <CODE>MazeBuilder</CODE> is<CODE>CountingMazeBuilder</CODE>. This builder doesn't create amaze at all; it just counts the different kinds of components thatwould have been created.</P><A NAME="auto1075"></A><PRE> class CountingMazeBuilder : public MazeBuilder { public: CountingMazeBuilder(); virtual void BuildMaze(); virtual void BuildRoom(int); virtual void BuildDoor(int, int); virtual void AddWall(int, Direction); void GetCounts(int&, int&) const; private: int _doors; int _rooms; };</PRE><A NAME="auto1076"></A><P>The constructor initializes the counters, and the overridden<CODE>MazeBuilder</CODE> operations increment them accordingly.</P><A NAME="auto1077"></A><PRE> CountingMazeBuilder::CountingMazeBuilder () { _rooms = _doors = 0; } void CountingMazeBuilder::BuildRoom (int) { _rooms++; } void CountingMazeBuilder::BuildDoor (int, int) { _doors++; } void CountingMazeBuilder::GetCounts ( int& rooms, int& doors ) const { rooms = _rooms; doors = _doors; }</PRE><A NAME="auto1078"></A><P>Here's how a client might use a <CODE>CountingMazeBuilder</CODE>:</P><A NAME="auto1079"></A><PRE> int rooms, doors; MazeGame game; CountingMazeBuilder builder; game.CreateMaze(builder); builder.GetCounts(rooms, doors); cout << "The maze has " << rooms << " rooms and " << doors << " doors" << endl;</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="auto1080"></A><P>The RTF converter application is from ET++ [<A HREF="bibfs.htm#et++" TARGET="_mainDisplayFrame">WGM88</A>]. Its text building block uses abuilder to process text stored in the RTF format.</P><A NAME="et-use-builder"></A><A NAME="smalltalk-use-builder"></A><P>Builder is a common pattern inSmalltalk-80 [<A HREF="bibfs.htm#parcplace_smalltalk" TARGET="_mainDisplayFrame">Par90</A>]:</P><UL><A NAME="auto1081"></A><LI>The Parser class in the compiler subsystem is a Director that takes aProgramNodeBuilder object as an argument. A Parser object notifiesits ProgramNodeBuilder object each time it recognizes a syntacticconstruct. When the parser is done, it asks the builder for the parsetree it built and returns it to the client.</LI><A NAME="auto1082"></A><P></P><A NAME="auto1083"></A><LI>ClassBuilder is a builder that Classes use to create subclasses forthemselves. In this case a Class is both the Director and theProduct.</LI><A NAME="auto1084"></A><P></P><A NAME="auto1085"></A><LI>ByteCodeStream is a builder that creates a compiled method as a bytearray. ByteCodeStream is a nonstandard use of the Builder pattern,because the complex object it builds is encoded as a byte array, notas a normal Smalltalk object. But the interface to ByteCodeStream istypical of a builder, and it would be easy to replace ByteCodeStreamwith a different class that represented programs as a compositeobject.</LI></UL><A NAME="auto1086"></A><P>The Service Configurator framework from the Adaptive CommunicationsEnvironment uses a builder to construct network service componentsthat are linked into a server at run-time [<A HREF="bibfs.htm#schmidt94" TARGET="_mainDisplayFrame">SS94</A>]. The components are describedwith a configuration language that's parsed by an LALR(1) parser.The semantic actions of the parser perform operations on the builderthat add information to the service component. In this case, theparser is the Director.</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="auto1087"></A><P><A HREF="pat3afs.htm" TARGET="_mainDisplayFrame">Abstract Factory (87)</A> issimilar to Builder in that it too may construct complex objects.The primary difference is that the Builder pattern focuses onconstructing a complex object step by step. Abstract Factory'semphasis is on families of product objects (either simple orcomplex). Builder returns the product as a final step, but as faras the Abstract Factory pattern is concerned, the product getsreturned immediately.</P><A NAME="auto1088"></A><P>A <A HREF="pat4cfs.htm" TARGET="_mainDisplayFrame">Composite (163)</A> is what thebuilder often builds.</P><A NAME="last"></A><P><A HREF="#intent"><IMG SRC="gifsb/up3.gif" BORDER=0></A><BR><A HREF="pat3cfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/rightar3.gif" ALIGN=TOP BORDER=0></A> <A HREF="pat3cfs.htm" TARGET="_mainDisplayFrame">Factory Method</A><BR><A HREF="pat3afs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/leftarr3.gif" ALIGN=TOP BORDER=0></A> <A HREF="pat3afs.htm" TARGET="_mainDisplayFrame">Abstract Factory</A></P></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -