📄 pat3d.htm
字号:
clone's components are clones of the prototype's components. Cloning
forces you to decide what if anything will be shared.
<A NAME="auto1041"></A>
<P>If objects in the system provide Save and Load operations, then you
can use them to provide a default implementation of Clone simply by
saving the object and loading it back immediately. The Save operation
saves the object into a memory buffer, and Load creates a duplicate by
reconstructing the object from the buffer.
</LI>
<A NAME="auto1042"></A>
<P></P>
<A NAME="auto1043"></A>
<LI><EM>Initializing clones.</EM>
While some clients are perfectly happy with the clone as is, others
will want to initialize some or all of its internal state to values of
their choosing. You generally can't pass these values in the Clone
operation, because their number will vary between classes of
prototypes. Some prototypes might need multiple initialization
parameters; others won't need any. Passing parameters in the Clone
operation precludes a uniform cloning interface.
<A NAME="auto1044"></A>
<P>It might be the case that your prototype classes already define
operations for (re)setting key pieces of state. If so, clients may
use these operations immediately after cloning. If not, then you may
have to introduce an <CODE>Initialize</CODE> operation (see the Sample
Code section) that takes initialization parameters as arguments and
sets the clone's internal state accordingly. Beware of deep-copying
Clone operations—the copies may have to be deleted (either
explicitly or within <CODE>Initialize</CODE>) before you reinitialize
them.
</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="auto1045"></A>
<P>We'll define a <CODE>MazePrototypeFactory</CODE> subclass of the
<CODE>MazeFactory</CODE> class (<A HREF="pat3afs.htm#MazeFactory-def" tppabs="http://ultra/development/DesignPatterns/hires/pat3afs.htm#MazeFactory-def" TARGET="_mainDisplayFrame">page 92</A>).
<CODE>MazePrototypeFactory</CODE> will be initialized with prototypes of the
objects it will create so that we don't have to subclass it just to
change the classes of walls or rooms it creates.</P>
<A NAME="auto1046"></A>
<P><CODE>MazePrototypeFactory</CODE> augments the <CODE>MazeFactory</CODE>
interface with a constructor that takes the prototypes as arguments:</P>
<A NAME="auto1047"></A>
<PRE>
class MazePrototypeFactory : public MazeFactory {
public:
MazePrototypeFactory(Maze*, Wall*, Room*, Door*);
virtual Maze* MakeMaze() const;
virtual Room* MakeRoom(int) const;
virtual Wall* MakeWall() const;
virtual Door* MakeDoor(Room*, Room*) const;
private:
Maze* _prototypeMaze;
Room* _prototypeRoom;
Wall* _prototypeWall;
Door* _prototypeDoor;
};
</PRE>
<A NAME="auto1048"></A>
<P>The new constructor simply initializes its prototypes:</P>
<A NAME="auto1049"></A>
<PRE>
MazePrototypeFactory::MazePrototypeFactory (
Maze* m, Wall* w, Room* r, Door* d
) {
_prototypeMaze = m;
_prototypeWall = w;
_prototypeRoom = r;
_prototypeDoor = d;
}
</PRE>
<A NAME="auto1050"></A>
<P>The member functions for creating walls, rooms, and doors are similar:
Each clones a prototype and then initializes it. Here are the
definitions of <CODE>MakeWall</CODE> and <CODE>MakeDoor</CODE>:</P>
<A NAME="auto1051"></A>
<PRE>
Wall* MazePrototypeFactory::MakeWall () const {
return _prototypeWall->Clone();
}
Door* MazePrototypeFactory::MakeDoor (Room* r1, Room *r2) const {
Door* door = _prototypeDoor->Clone();
door->Initialize(r1, r2);
return door;
}
</PRE>
<A NAME="auto1052"></A>
<P>We can use <CODE>MazePrototypeFactory</CODE> to create a prototypical or
default maze just by initializing it with prototypes of basic maze
components:</P.
<A NAME="auto1053"></A>
<PRE>
MazeGame game;
MazePrototypeFactory simpleMazeFactory(
new Maze, new Wall, new Room, new Door
);
Maze* maze = game.CreateMaze(simpleMazeFactory);
</PRE>
<A NAME="auto1054"></A>
<P>To change the type of maze, we initialize
<CODE>MazePrototypeFactory</CODE> with a different set of prototypes.
The following call creates a maze with a <CODE>BombedDoor</CODE> and a
<CODE>RoomWithABomb</CODE>:</P>
<A NAME="auto1055"></A>
<PRE>
MazePrototypeFactory bombedMazeFactory(
new Maze, new BombedWall,
new RoomWithABomb, new Door
);
</PRE>
<A NAME="door-proto"></A>
<P>An object that can be used as a prototype, such as an instance of
<CODE>Wall</CODE>, must support the <CODE>Clone</CODE> operation. It must
also have a copy constructor for cloning. It may also need a separate
operation for reinitializing internal state. We'll add the
<CODE>Initialize</CODE> operation to <CODE>Door</CODE> to let clients
initialize the clone's rooms.</P>
<A NAME="auto1056"></A>
<P>Compare the following definition of <CODE>Door</CODE> to the one on
<A HREF="chap3fs.htm#def-room" tppabs="http://ultra/development/DesignPatterns/hires/chap3fs.htm#def-room" TARGET="_mainDisplayFrame">page 83</A>:
<A NAME="auto1057"></A>
<PRE>
class Door : public MapSite {
public:
Door();
Door(const Door&);
virtual void Initialize(Room*, Room*);
virtual Door* Clone() const;
virtual void Enter();
Room* OtherSideFrom(Room*);
private:
Room* _room1;
Room* _room2;
};
Door::Door (const Door& other) {
_room1 = other._room1;
_room2 = other._room2;
}
void Door::Initialize (Room* r1, Room* r2) {
_room1 = r1;
_room2 = r2;
}
Door* Door::Clone () const {
return new Door(*this);
}
</PRE>
<A NAME="auto1058"></A>
<P>The <CODE>BombedWall</CODE> subclass must override <CODE>Clone</CODE> and
implement a corresponding copy constructor.</P>
<A NAME="auto1059"></A>
<PRE>
class BombedWall : public Wall {
public:
BombedWall();
BombedWall(const BombedWall&);
virtual Wall* Clone() const;
bool HasBomb();
private:
bool _bomb;
};
BombedWall::BombedWall (const BombedWall& other) : Wall(other) {
_bomb = other._bomb;
}
Wall* BombedWall::Clone () const {
return new BombedWall(*this);
}
</PRE>
<A NAME="auto1060"></A>
<P>Although <CODE>BombedWall::Clone</CODE> returns a <CODE>Wall*</CODE>, its
implementation returns a pointer to a new instance of a subclass, that
is, a <CODE>BombedWall*</CODE>. We define <CODE>Clone</CODE> like this in
the base class to ensure that clients that clone the prototype don't
have to know about their concrete subclasses. Clients should never
need to downcast the return value of <CODE>Clone</CODE> to the desired
type.</P>
<A NAME="auto1061"></A>
<P>In Smalltalk, you can reuse the standard <CODE>copy</CODE> method
inherited from <CODE>Object</CODE> to clone any <CODE>MapSite</CODE>. You
can use <CODE>MazeFactory</CODE> to produce the prototypes you'll need;
for example, you can create a room by supplying the name
<CODE>#room</CODE>. The <CODE>MazeFactory</CODE> has a dictionary
that maps names to prototypes. Its <CODE>make:</CODE> method looks like
this:</P>
<A NAME="auto1062"></A>
<PRE>
make: partName
^ (partCatalog at: partName) copy
</PRE>
<A NAME="auto1063"></A>
<P>Given appropriate methods for initializing the <CODE>MazeFactory</CODE>
with prototypes, you could create a simple maze with the following code:</P>
<A NAME="auto1064"></A>
<PRE>
CreateMaze
on: (MazeFactory new
with: Door new named: #door;
with: Wall new named: #wall;
with: Room new named: #room;
yourself)
</PRE>
<A NAME="auto1065"></A>
<P>where the definition of the <CODE>on:</CODE> class method for
<CODE>CreateMaze</CODE> would be</P>
<A NAME="auto1066"></A>
<PRE>
on: aFactory
| room1 room2 |
room1 := (aFactory make: #room) location: 1@1.
room2 := (aFactory make: #room) location: 2@1.
door := (aFactory make: #door) from: room1 to: room2.
room1
atSide: #north put: (aFactory make: #wall);
atSide: #east put: door;
atSide: #south put: (aFactory make: #wall);
atSide: #west put: (aFactory make: #wall).
room2
atSide: #north put: (aFactory make: #wall);
atSide: #east put: (aFactory make: #wall);
atSide: #south put: (aFactory make: #wall);
atSide: #west put: door.
^ Maze new
addRoom: room1;
addRoom: room2;
yourself
</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="sutherland"></A>
<P>Perhaps the first example of the Prototype pattern was in Ivan
Sutherland's Sketchpad system [<A HREF="bibfs.htm#sketchpad" tppabs="http://ultra/development/DesignPatterns/hires/bibfs.htm#sketchpad" TARGET="_mainDisplayFrame">Sut63</A>]. The first widely known
application of the pattern in an object-oriented language was in
ThingLab, where users could form a composite object and then promote
it to a prototype by installing it in a library of reusable
objects [<A HREF="bibfs.htm#Borning-ThingLab_toplas81" tppabs="http://ultra/development/DesignPatterns/hires/bibfs.htm#Borning-ThingLab_toplas81" TARGET="_mainDisplayFrame">Bor81</A>].
Goldberg and Robson mention prototypes as a pattern [<A HREF="bibfs.htm#st_lang" tppabs="http://ultra/development/DesignPatterns/hires/bibfs.htm#st_lang" TARGET="_mainDisplayFrame">GR83</A>], but Coplien [<A HREF="bibfs.htm#coplien_idioms" tppabs="http://ultra/development/DesignPatterns/hires/bibfs.htm#coplien_idioms" TARGET="_mainDisplayFrame">Cop92</A>] gives a much more complete
description. He describes idioms related to the Prototype pattern
for C++ and gives many examples and variations.</P>
<A NAME="et-use-adapter"></A>
<A NAME="etgdb"></A>
<A NAME="gdb"></A>
<A NAME="gnugdb"></A>
<P>Etgdb is a debugger front-end based on ET++ that provides a
point-and-click interface to different line-oriented debuggers.
Each debugger has a corresponding DebuggerAdaptor subclass. For
example, GdbAdaptor adapts etgdb to the command syntax of GNU gdb,
while SunDbxAdaptor adapts etgdb to Sun's dbx debugger. Etgdb does
not have a set of DebuggerAdaptor classes hard-coded into it.
Instead, it reads the name of the adaptor to use from an environment
variable, looks for a prototype with the specified name in a global
table, and then clones the prototype. New debuggers can be added
to etgdb by linking it with the DebuggerAdaptor that works for that
debugger.</P>
<A NAME="auto1067"></A>
<P>The "interaction technique library" in Mode Composer stores
prototypes of objects that support various interaction techniques [<A HREF="bibfs.htm#mode" tppabs="http://ultra/development/DesignPatterns/hires/bibfs.htm#mode" TARGET="_mainDisplayFrame">Sha90</A>]. Any interaction technique created
by the Mode Composer can be used as a prototype by placing it in
this library. The Prototype pattern lets Mode Composer support an
unlimited set of interaction techniques.</P>
<A NAME="unidraw-use-proto"></A>
<P>The music editor example discussed earlier is based on the
Unidraw drawing framework [<A HREF="bibfs.htm#unidraw_framework" tppabs="http://ultra/development/DesignPatterns/hires/bibfs.htm#unidraw_framework" TARGET="_mainDisplayFrame">VL90</A>].</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="auto1068"></A>
<P>Prototype and <A HREF="pat3afs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat3afs.htm" TARGET="_mainDisplayFrame">Abstract
Factory (87)</A> are competing patterns in some ways, as we
discuss at the end of this chapter. They can also be used together,
however. An Abstract Factory might store a set of prototypes from
which to clone and return product objects.</P>
<A NAME="auto1069"></A>
<P>Designs that make heavy use of the <A HREF="pat4cfs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat4cfs.htm"
TARGET="_mainDisplayFrame">Composite (163)</A> and <A HREF="pat4dfs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat4dfs.htm"
TARGET="_mainDisplayFrame">Decorator (175)</A> patterns often can benefit
from Prototype as well.</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="pat3efs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat3efs.htm" TARGET="_mainDisplayFrame"><IMG SRC="rightar3.gif" tppabs="http://ultra/development/DesignPatterns/hires/gifsb/rightar3.gif"
ALIGN=TOP BORDER=0></A> <A HREF="pat3efs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat3efs.htm"
TARGET="_mainDisplayFrame">Singleton</A><BR>
<A HREF="pat3cfs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat3cfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="leftarr3.gif" tppabs="http://ultra/development/DesignPatterns/hires/gifsb/leftarr3.gif"
ALIGN=TOP BORDER=0></A> <A HREF="pat3cfs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat3cfs.htm"
TARGET="_mainDisplayFrame">Factory Method</A>
</P>
<HR>
<A NAME="footnote1"></A>
<P><SUP>1</SUP>Such applications reflect the <A HREF="pat4cfs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat4cfs.htm"
TARGET="_mainDisplayFrame">Composite (163)</A> and <A HREF="pat4dfs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat4dfs.htm"
TARGET="_mainDisplayFrame">Decorator (175)</A>
patterns.
</P>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -