在面向对象的程序开发中,复制功能是非常有意义的。很多时候构造一个对象会很复杂,需要设定很多个参数,并且调用很多个方法。如果这个对象需要很多个实例,那么重复进行复杂的创建过程就非常容易出错,对于这类问题的一个很好的解决模式就是克隆系统中的已有对象,然后对其属性进行少量修改或不作修改,这就是原型模式。
     当然作为克隆的实现方法一般有两种:浅拷贝(shallow copy)与深拷贝(deep copy)。至于什么是shallow copy,什么是deep copy ?请查找相关资料,这里不累诉了。
    下面我们看看Prototype模式的UML图:


      抽象原型(Prototype)角色:这是一个抽象角色,这里为ComputerPrototype类,通常由一个C#接口或抽象类实现。此角色给出所有的具体原型类所需的接口。在C#中,抽象原型角色通常实现了ICloneable接口。
      具体原型(Concrete Prototype)角色:被复制的对象,这里为DELLPrototype,IBMPrototype类。此角色需要实现抽象原型角色所要求的接口。

这里我们用到了上章Builder模式举的例子,当然做了一些简化(把具体构造过程合并为了各自的构造函数)。
我们来看看shallow copy和deep copy两种不同形式的Prototype模式实现,具体代码如下(C#):
 1.Shallow Copy(C#):

  1using System;
  2using System.Collections;
  3
  4namespace PrototypeShallowCopy
  5{
  6    /// <summary>
  7    ///============== Program Description==============
  8    ///Name:PrototypeShallowCopy.cs
  9    ///Objective:PrototypeShallowCopy 
 10    ///Date:2006-04-30
 11    ///Written By coffee.liu
 12    ///================================================
 13    /// </summary>

 14    class Class1
 15    {
 16        /// <summary>
 17        /// 应用程序的主入口点。
 18        /// </summary>

 19        [STAThread]
 20        static void Main(string[] args)
 21        {
 22            Hashtable PH1,PH2,PH3,PH4;
 23            DellPrototype P1=new DellPrototype();
 24            PH1=(P1.GetComputer()) as Hashtable;//得到P1的Hashtable
 25            DellPrototype P2=(DellPrototype)P1.Clone();
 26            PH2=(P2.GetComputer()) as Hashtable;//得到克隆P2的Hashtable
 27            Console.WriteLine("Dell Computers..");
 28            Console.WriteLine("Board:"+Convert.ToString(PH2["Board"]));
 29            Console.WriteLine("CPU:"+Convert.ToString(PH2["CPU"]));
 30            Console.WriteLine("Menory:"+Convert.ToString(PH2["Menory"]));
 31            if (PH1==PH2)
 32                Console.WriteLine("we are the same HashTable");//查看潜表拷贝的效果
 33           ///////////
 34            IBMPrototype P3=new IBMPrototype();
 35            PH3=(P3.GetComputer()) as Hashtable;//得到P3的Hashtable
 36            IBMPrototype P4=(IBMPrototype)P3.Clone();
 37            PH4=(P4.GetComputer()) as Hashtable;//得到克隆P4的Hashtable
 38            Console.WriteLine("Dell Computers..");
 39            Console.WriteLine("Board:"+Convert.ToString(PH4["Board"]));
 40            Console.WriteLine("CPU:"+Convert.ToString(PH4["CPU"]));
 41            Console.WriteLine("Menory:"+Convert.ToString(PH4["Menory"])); 
 42            if (PH3==PH4)
 43                Console.WriteLine("we are the same HashTable");//查看潜表拷贝的效果
 44
 45
 46        }

 47    }

 48    //abstractPrototype
 49    abstract class ComputerPrototype:ICloneable
 50    {
 51        abstract public Object GetComputer();
 52        ICloneable 成员
 61    }

 62    class DellPrototype:ComputerPrototype
 63    {
 64        private Hashtable myHashTable;
 65        public DellPrototype()
 66        {
 67               myHashTable=new Hashtable();
 68               myHashTable.Add("Board","DellBoard");
 69               myHashTable.Add("CPU","Intel");
 70               myHashTable.Add("Menory","DellMenory");
 71        }

 72
 73        public override Object GetComputer()
 74        {
 75            return myHashTable;
 76        }

 77        ICloneable 成员
 86    }

 87    class IBMPrototype:ComputerPrototype
 88    {
 89        private Hashtable myHashTable;
 90        public IBMPrototype()
 91        {
 92            myHashTable=new Hashtable();
 93            myHashTable.Add("Board","IBMBoard");
 94            myHashTable.Add("CPU","Intel");
 95            myHashTable.Add("Menory","IBMMenory");
 96        }

 97        public override Object GetComputer()
 98        {
 99            return myHashTable;
100        }

101        ICloneable 成员
109    }

110
111}

112

下面看看Pascal的shallow Copy的实现:
  1program Prototype;
  2
  3        //============== Program Description==============
  4    //Name:Prototype.dpr
  5    //Objective:Prototype
  6    //Date:2006-05-01
  7    //Written By coffee.liu
  8    //================================================
  9{$APPTYPE CONSOLE}
 10
 11uses
 12  SysUtils,Classes;
 13  //零件
 14  type Part=record
 15       Name:string;
 16       Valual:string;
 17  end;
 18  PPart=^Part;
 19  //具体产品
 20  type Product=class
 21      private
 22      AProduct:TList;
 23      public 
 24      constructor Create;
 25      procedure Add(APart:PPart);
 26      procedure Say();
 27      destructor Destroy();override;
 28  end;
 29  //建造者
 30  type TPrototype=class(TPersistent)
 31         procedure CreateComputer;virtual;abstract;
 32         function GetComputer:Tobject;virtual;abstract;
 33  end;
 34  //具体建造者
 35  type DellPrototype=class(TPrototype)
 36         private AProduct:Product;
 37         public
 38          procedure CreateComputer;override;
 39         function GetComputer:Tobject;override;
 40        // constructor Create();
 41         destructor Destroy();override;
 42         procedure Assign(Source:TPersistent);override;
 43
 44  end;
 45  //具体建造者
 46  type IBMPrototype=class(TPrototype)
 47         private AProduct:Product;
 48         public
 49          procedure CreateComputer;override;
 50         function GetComputer:Tobject;override;
 51        // constructor Create();
 52         destructor Destroy();override;
 53          procedure Assign(Source:TPersistent);override;
 54  end;
 55{ DellPrototype }
 56procedure DellPrototype.Assign(Source: TPersistent);
 57var
 58aSOBJ:DellPrototype;
 59begin
 60//  inherited;
 61     aSOBJ:=DellPrototype(Source);
 62   // self.AProduct:=Product.Create;
 63    self.AProduct:=aSOBJ.AProduct;
 64end;
 65
 66procedure DellPrototype.CreateComputer;
 67var
 68P1,P2,P3:PPart;
 69begin
 70  inherited;
 71    AProduct:=Product.Create;
 72   new(P1); //创建指针,以下同
 73  P1^.Name:='DELL';
 74  P1^.Valual:='Board';
 75  AProduct.Add(p1);
 76  ///////////
 77   new(P2);
 78  P2^.Name:='DELL';
 79  P2^.Valual:='Cpu';
 80  AProduct.Add(p2);
 81  /////////////
 82   new(P3);
 83  P3^.Name:='DELL';
 84  P3^.Valual:='Menory';
 85  AProduct.Add(p3);
 86end;
 87
 88destructor DellPrototype.Destroy;
 89begin
 90   AProduct.Free;
 91   inherited Destroy;
 92end;
 93
 94function DellPrototype.GetComputer: Tobject;
 95begin
 96    result:=AProduct;
 97end;
 98
 99{ IBMPrototype }
100procedure IBMPrototype.Assign(Source: TPersistent);
101var
102aSOBJ:IBMPrototype;
103begin
104//  inherited;
105      aSOBJ:=IBMPrototype(Source);
106   // self.AProduct:=Product.Create;
107    self.AProduct:=aSOBJ.AProduct;
108end;
109
110
111
112
113procedure IBMPrototype.CreateComputer;
114var
115P1,P2,P3:PPart;
116begin
117  inherited;
118      AProduct:=Product.Create;
119  new(P1);
120  P1^.Name:='IBM';
121  P1^.Valual:='Board';
122  AProduct.Add(p1);
123  //////////////
124  new(P2);
125  P2^.Name:='IBM';
126  P2^.Valual:='Cpu';
127  AProduct.Add(p2);
128  /////////////
129   new(P3);
130  P3^.Name:='IBM';
131  P3^.Valual:='Menory';
132  AProduct.Add(p3);
133end;
134
135destructor IBMPrototype.Destroy;
136begin
137    AProduct.Free;
138    inherited Destroy;
139end;
140
141function IBMPrototype.GetComputer: Tobject;
142begin
143      result:=AProduct;
144end;
145{ Product }
146
147procedure Product.Add(APart: PPart);
148begin
149   AProduct.Add(APart);
150end;
151
152constructor Product.Create;
153begin
154  AProduct:=TList.Create;
155end;
156
157destructor Product.Destroy;
158begin
159
160   AProduct.Free;
161   inherited Destroy;
162end;
163
164procedure Product.Say;
165var
166i:integer;
167begin
168try
169  for i:=0 to AProduct.Count-1 do
170     begin
171   Writeln(string(PPart(AProduct.Items[i])^.Name)+':'+PPart(AProduct.Items[i])^.Valual);
172     Dispose(PPart(AProduct.Items[i]));//释放指针
173     end;
174except
175   on EAccessViolation do
176     Writeln('Some Object had been destoried');
177end;
178end;
179var
180APrototype,BPrototype,CPrototype,DPrototype:TPrototype;
181DELLProduct,DELLShallowProduct,IBMProduct,IBMShallowProduct:Product;
182
183begin
184try
185     APrototype:=DellPrototype.Create;
186     APrototype.CreateComputer;
187     DELLProduct:=Product(APrototype.GetComputer);
188     DELLProduct.Say;
189     ////////////
190     BPrototype:=DellPrototype.Create;
191     BPrototype.Assign(APrototype);
192     DELLShallowProduct:=Product(BPrototype.GetComputer);
193     DELLShallowProduct.Say;
194     //////////////
195     CPrototype:=IBMPrototype.Create;
196     CPrototype.CreateComputer;
197     IBMProduct:=Product(CPrototype.GetComputer);
198     IBMProduct.Say;
199     //////////////
200     DPrototype:=IBMPrototype.Create;
201     DPrototype.Assign(CPrototype);
202     IBMShallowProduct:=Product(DPrototype.GetComputer);
203     IBMShallowProduct.Say;
204     finally
205      DELLProduct.Free;
206      IBMProduct.Free;
207     end;
208end.

由于Delphi没有Clone方法,所以实现起来我们需要使用到一个比较特殊的类TPersistent,TPersistent除了提供了可持久性的功能以外,它还有个非常重要的虚拟方法Assign,这个方法就实现了类似于C#的clone方法,其作用就是把一个源对象的属性复制到目标对象中,于C#有所不同的是在调用Assign方法之前,需要先Create一个目标对象,然后再复制源对象。默认的TPersistent的Assign方法只是简单地调用源对象的AssignTo方法来复制属性,而TPersistent的AssignTo虚方法也只是简单地抛出一个异常。也就是说TPersistent方法并没有实现任何有意义的功能,那么对于派生自TPersistent类的对象要想提供克隆的功能都需要重载Assign或者AssignTo方法来实现自定义的复制功能。上面的56~64,100~108行就是对DellPrototype和IBMPrototype的Assign方法的重写。我们的第190行创建的BPrototype实际上并没用用到CreateComputer来初始化类,而是直接用了APtototype的值来初始化了。
       为了证实我们的DellPrototype和IBMPrototype只是一个Shallow Copy,看看172行!我们把在185行创建的APrototype中的Product对象给释放掉了!而BPrototype中的Product还指向刚刚释放调的APrototype中的Product,哄哄!系统就会报EAccessViolation错误!
       当然要想系统能够显示出所有对象的信息,只要把172行注释掉就OK了。
2. Deep Copy(C#):
  1using System;
  2using System.Collections;
  3
  4namespace PrototypeDeepCopy
  5{
  6    /// <summary>
  7    ///============== Program Description==============
  8    ///Name:PrototypeDeepCopy.cs
  9    ///Objective:PrototypeDeepCopy 
 10    ///Date:2006-04-30
 11    ///Written By coffee.liu
 12    ///================================================
 13    /// </summary>

 14    class Class1
 15    {
 16        /// <summary>
 17        /// 应用程序的主入口点。
 18        /// </summary>

 19        [STAThread]
 20        static void Main(string[] args)
 21        {
 22            Hashtable PH1,PH2,PH3,PH4;
 23            DellPrototype P1=new DellPrototype();
 24            PH1=(P1.GetComputer()) as Hashtable;//得到P1的Hashtable
 25            DellPrototype P2=(DellPrototype)P1.Clone();
 26            PH2=(P2.GetComputer()) as Hashtable;//得到克隆P2的Hashtable
 27            Console.WriteLine("Dell Computers..");
 28            Console.WriteLine("Board:"+Convert.ToString(PH2["Board"]));
 29            Console.WriteLine("CPU:"+Convert.ToString(PH2["CPU"]));
 30            Console.WriteLine("Menory:"+Convert.ToString(PH2["Menory"]));
 31            if (PH1!=PH2)
 32                Console.WriteLine("we are not the same HashTable");//查看深度复制的效果
 33            ///////////
 34            IBMPrototype P3=new IBMPrototype();
 35            PH3=(P3.GetComputer()) as Hashtable;//得到P3的Hashtable
 36            IBMPrototype P4=(IBMPrototype)P3.Clone();
 37            PH4=(P4.GetComputer()) as Hashtable;//得到克隆P4的Hashtable
 38            Console.WriteLine("Dell Computers..");
 39            Console.WriteLine("Board:"+Convert.ToString(PH4["Board"]));
 40            Console.WriteLine("CPU:"+Convert.ToString(PH4["CPU"]));
 41            Console.WriteLine("Menory:"+Convert.ToString(PH4["Menory"])); 
 42            if (PH3!=PH4)
 43                Console.WriteLine("we are not the same HashTable");//查看深度复制的效果
 44
 45        }

 46    }

 47    //abstractPrototype
 48    abstract class ComputerPrototype:ICloneable
 49    {
 50        abstract public Object GetComputer();
 51        ICloneable 成员
 60    }

 61    class DellPrototype:ComputerPrototype
 62    {
 63        private Hashtable myHashTable;
 64        private DellPrototype(Hashtable HT){
 65        
 66        this.myHashTable=(Hashtable)HT.Clone();
 67        }

 68        public DellPrototype()
 69        {
 70            myHashTable=new Hashtable();
 71            myHashTable.Add("Board","DellBoard");
 72            myHashTable.Add("CPU","Intel");
 73            myHashTable.Add("Menory","DellMenory");
 74        }

 75       
 76        public override Object GetComputer()
 77        {
 78            return myHashTable;
 79        }

 80        ICloneable 成员
 89    }

 90    class IBMPrototype:ComputerPrototype
 91    {
 92        private Hashtable myHashTable;
 93        private IBMPrototype(Hashtable HT)
 94        {
 95        
 96            this.myHashTable=(Hashtable)HT.Clone();
 97        }

 98        public IBMPrototype()
 99        {
100            myHashTable=new Hashtable();
101            myHashTable.Add("Board","IBMBoard");
102            myHashTable.Add("CPU","Intel");
103            myHashTable.Add("Menory","IBMMenory");
104        }

105        public override Object GetComputer()
106        {
107            return myHashTable;
108        }

109        ICloneable 成员
117    }

118}

119
当然以上Deep Copy中对HashTable的操作还只是值的复制,如果HashTable中存放的是引用类型可能还要做进一步的复制工作。用C#来实现Prototype模式还是比较简单的。
    下面我们看看如何用Pascal来实现同样功能的Deep Copy:
  1program PrototypeDeep;
  2
  3        //============== Program Description==============
  4    //Name:PrototypeDeep.dpr
  5    //Objective:PrototypeDeep
  6    //Date:2006-05-01
  7    //Written By coffee.liu
  8    //================================================
  9{$APPTYPE CONSOLE}
 10
 11uses
 12  SysUtils,Classes;
 13  //零件
 14  type Part=record
 15       Name:string;
 16       Valual:string;
 17  end;
 18  PPart=^Part;
 19  //具体产品
 20  type Product=class(TPersistent)
 21      private
 22      AProduct:TList;
 23      public 
 24      constructor Create;
 25      procedure Add(APart:PPart);
 26      procedure Say();
 27      destructor Destroy();override;
 28      procedure Assign(Source:TPersistent);override; //深度复制
 29  end;
 30  //建造者
 31  type TPrototype=class(TPersistent)
 32         procedure CreateComputer;virtual;abstract;
 33         function GetComputer:Tobject;virtual;abstract;
 34  end;
 35  //具体建造者
 36  type DellPrototype=class(TPrototype)
 37         private AProduct:Product;
 38         public
 39          procedure CreateComputer;override;
 40         function GetComputer:Tobject;override;
 41        // constructor Create();
 42         destructor Destroy();override;
 43         procedure Assign(Source:TPersistent);override;
 44
 45  end;
 46  //具体建造者
 47  type IBMPrototype=class(TPrototype)
 48         private AProduct:Product;
 49         public
 50          procedure CreateComputer;override;
 51         function GetComputer:Tobject;override;
 52        // constructor Create();
 53         destructor Destroy();override;
 54          procedure Assign(Source:TPersistent);override;
 55  end;
 56{ DellPrototype }
 57procedure DellPrototype.Assign(Source: TPersistent);
 58var
 59aSOBJ:DellPrototype;
 60begin
 61//  inherited;
 62     aSOBJ:=DellPrototype(Source);
 63    self.AProduct:=Product.Create;
 64    self.AProduct.Assign(aSOBJ.AProduct);
 65end;
 66
 67procedure DellPrototype.CreateComputer;
 68var
 69P1,P2,P3:PPart;
 70begin
 71  inherited;
 72    AProduct:=Product.Create;
 73   new(P1); //创建指针,以下同
 74  P1^.Name:='DELL';
 75  P1^.Valual:='Board';
 76  AProduct.Add(p1);
 77  ///////////
 78   new(P2);
 79  P2^.Name:='DELL';
 80  P2^.Valual:='Cpu';
 81  AProduct.Add(p2);
 82  /////////////
 83   new(P3);
 84  P3^.Name:='DELL';
 85  P3^.Valual:='Menory';
 86  AProduct.Add(p3);
 87end;
 88
 89destructor DellPrototype.Destroy;
 90begin
 91   AProduct.Free;
 92   inherited Destroy;
 93end;
 94
 95function DellPrototype.GetComputer: Tobject;
 96begin
 97    result:=AProduct;
 98end;
 99
100{ IBMPrototype }
101procedure IBMPrototype.Assign(Source: TPersistent);
102var
103aSOBJ:IBMPrototype;
104begin
105//  inherited;
106      aSOBJ:=IBMPrototype(Source);
107    self.AProduct:=Product.Create;
108    self.AProduct.Assign(aSOBJ.AProduct);
109end;
110
111
112
113
114procedure IBMPrototype.CreateComputer;
115var
116P1,P2,P3:PPart;
117begin
118  inherited;
119      AProduct:=Product.Create;
120  new(P1);
121  P1^.Name:='IBM';
122  P1^.Valual:='Board';
123  AProduct.Add(p1);
124  //////////////
125  new(P2);
126  P2^.Name:='IBM';
127  P2^.Valual:='Cpu';
128  AProduct.Add(p2);
129  /////////////
130   new(P3);
131  P3^.Name:='IBM';
132  P3^.Valual:='Menory';
133  AProduct.Add(p3);
134end;
135
136destructor IBMPrototype.Destroy;
137begin
138    AProduct.Free;
139    inherited Destroy;
140end;
141
142function IBMPrototype.GetComputer: Tobject;
143begin
144      result:=AProduct;
145end;
146{ Product }
147
148procedure Product.Add(APart: PPart);
149begin
150   AProduct.Add(APart);
151end;
152
153procedure Product.Assign(Source: TPersistent);
154var
155aSOBJ:Product;
156i:integer;
157PX:array of PPart;
158begin
159      aSOBJ:=Product(Source);
160      setlength(PX,aSOBJ.AProduct.Count);
161   for i:=0 to aSOBJ.AProduct.Count-1 do
162     begin
163         new(PX[i]);
164  PX[i]^.Name:=PPart(aSOBJ.AProduct.Items[i])^.Name;
165  PX[i]^.Valual:=PPart(aSOBJ.AProduct.Items[i])^.Valual;
166         self.AProduct.Add(PX[i]);
167     end;
168end;
169
170constructor Product.Create;
171begin
172  AProduct:=TList.Create;
173end;
174
175destructor Product.Destroy;
176begin
177
178   AProduct.Free;
179   inherited Destroy;
180end;
181
182procedure Product.Say;
183var
184i:integer;
185begin
186try
187  for i:=0 to AProduct.Count-1 do
188     begin
189   Writeln(string(PPart(AProduct.Items[i])^.Name)+':'+PPart(AProduct.Items[i])^.Valual);
190     Dispose(PPart(AProduct.Items[i]));//释放指针
191     end;
192except
193   on EAccessViolation do
194     Writeln('Some Object had been destoried');
195end;
196end;
197var
198APrototype,BPrototype,CPrototype,DPrototype:TPrototype;
199DELLProduct,DELLShallowProduct,IBMProduct,IBMShallowProduct:Product;
200
201begin
202try
203     APrototype:=DellPrototype.Create;
204     APrototype.CreateComputer;
205     DELLProduct:=Product(APrototype.GetComputer);
206
207     ////////////
208     BPrototype:=DellPrototype.Create;
209     BPrototype.Assign(APrototype);
210     DELLShallowProduct:=Product(BPrototype.GetComputer);
211     DELLProduct.Say;
212     DELLShallowProduct.Say;
213     //////////////
214     CPrototype:=IBMPrototype.Create;
215     CPrototype.CreateComputer;
216     IBMProduct:=Product(CPrototype.GetComputer);
217
218     //////////////
219     DPrototype:=IBMPrototype.Create;
220     DPrototype.Assign(CPrototype);
221     IBMShallowProduct:=Product(DPrototype.GetComputer);
222     IBMProduct.Say;
223     IBMShallowProduct.Say;
224     finally
225      DELLProduct.Free;
226      IBMProduct.Free;
227     end;
228end.
当然同上面的Pascal实现shallow copy一样,要想实现Deep Copy必须要将我们的Product类也继承TPersistent,同时重写Assign,153~168行就是对Product类的Assign进行的重写。190行同样也是释放指针,但这次运行一下看看,不报错了!这就说明我们的Product中的TList已经不在是同一个对象了,从而实现了深度拷贝。
      用原型模式能根据需要克隆类,这样,在运行时就可以添加或删除类。根据程序运行情况,可以在运行时更改一个类的内部数据表示,也可以在运行时指定新对象而无需创建一个新类。
     Prototype模式的最主要缺点就是每一个类必须配备一个克隆方法。而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事。
posted on 2006-04-30 18:34  coffeeliu  阅读(1088)  评论(2编辑  收藏  举报