设计模式(6):原型模式

来源:互联网 发布:创新管理优化服务培育 编辑:程序博客网 时间:2024/05/17 22:58

本模式引用一个小例子(写简历)来加以说明,招聘会上通常会拿着好多份相同的简历去应聘不同的公司,现在用代码创建三份相同的简历:

首先定义简历 Resume 类,包含姓名、性别、年龄、工作时间、工作单位五种字段,包含设置个人信息、设置工作经历、显示三种方法:

   1: //简历
   2: class Resume
   3: {
   4:     private string name;
   5:     private string sex;
   6:     private string age;
   7:     private string timeArea;
   8:     private string company;
   9:  
  10:     public Resume(string name)
  11:     {
  12:         this.name = name;
  13:     }
  14:  
  15:     //设置个人信息
  16:     public void SetPersonalInfo(string sex, string age)
  17:     {
  18:         this.sex = sex;
  19:         this.age = age;
  20:     }
  21:  
  22:     //设置工作经历
  23:     public void SetWorkExperience(string timeArea, string company)
  24:     {
  25:         this.timeArea = timeArea;
  26:         this.company = company;
  27:     }
  28:  
  29:     //显示
  30:     public void Display()
  31:     {
  32:         Console.WriteLine("{0} {1} {2}", name, sex, age);
  33:         Console.WriteLine("工作经历:{0} {1}", timeArea, company);
  34:     }
  35:  
  36: }

 

现在要创建三份相同的简历,客户端代码:

   1: static void Main(string[] args)
   2: {
   3:     Resume a = new Resume("大鸟");
   4:     a.SetPersonalInfo("男", "29");
   5:     a.SetWorkExperience("1998-2000", "XX公司");
   6:  
   7:     Resume b = new Resume("大鸟");
   8:     b.SetPersonalInfo("男", "29");
   9:     b.SetWorkExperience("1998-2000", "XX公司");
  10:  
  11:     Resume c = new Resume("大鸟");
  12:     c.SetPersonalInfo("男", "29");
  13:     c.SetWorkExperience("1998-2000", "XX公司");
  14:  
  15:  
  16:     a.Display();
  17:     b.Display();
  18:     c.Display();
  19:  
  20:     Console.Read();
  21: }

 

运行结果:

1

 

很显然这不是最好的解决方案,包含重复代码。并且加入有二十份相同的简历,写完之后突然发现年份写错了,就要改二十遍。应对这种相同简历的情况,可以使用传引用(非传值)实现,代码如下:

   1: static void Main(string[] args)
   2: {
   3:     Resume a = new Resume("大鸟");
   4:     a.SetPersonalInfo("男", "29");
   5:     a.SetWorkExperience("1998-2000", "XX公司");
   6:  
   7:     Resume b = a;
   8:  
   9:     Resume c = a;
  10:     
  11:     a.Display();
  12:     b.Display();
  13:     c.Display();
  14:  
  15:     Console.Read();
  16: }

 

运行结果同上。现在由于不同单位对于求职者的兴趣点不同,应对不同公司的简历也略有差别,该怎么办?

这就是原型模式啦,所谓的原型模式,定义是用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节。

 

原型模式结构图:

2

 

基本的原型模式代码:

   1: abstract class Prototype
   2: {
   3:     private string id;
   4:  
   5:     public Prototype(string id)
   6:     {
   7:         this.id = id;
   8:     }
   9:  
  10:     public string Id
  11:     {
  12:         get { return id; }
  13:     }
  14:  
  15:     public abstract Prototype Clone();
  16: }
  17:  
  18: class ConcretePrototype1 : Prototype
  19: {
  20:     public ConcretePrototype1(string id)
  21:         : base(id)
  22:     {
  23:     }
  24:  
  25:     public override Prototype Clone()
  26:     {
  27:         return (Prototype)this.MemberwiseClone();
  28:     }
  29: }
  30:  
  31:  
  32: class ConcretePrototype2 : Prototype
  33: {
  34:     public ConcretePrototype2(string id)
  35:         : base(id)
  36:     {
  37:     }
  38:  
  39:     public override Prototype Clone()
  40:     {
  41:         return (Prototype)this.MemberwiseClone();
  42:     }
  43: }

其中,MemberwiseClone() 函数用于创建当前对象的浅表副本。方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型的,则复制引用但不复制引用的对象,因此,原始对象及其副本引用同一对象。

 

客户端代码:

   1: static void Main(string[] args)
   2: {
   3:     ConcretePrototype1 p1 = new ConcretePrototype1("I");
   4:     ConcretePrototype1 c1 = (ConcretePrototype1)p1.Clone();
   5:     Console.WriteLine("Cloned: {0}", c1.Id);
   6:  
   7:     ConcretePrototype2 p2 = new ConcretePrototype2("II");
   8:     ConcretePrototype2 c2 = (ConcretePrototype2)p2.Clone();
   9:     Console.WriteLine("Cloned: {0}", c2.Id);
  10:  
  11:     Console.Read();
  12: }

其实,对于 .NET 而言,那个原型抽象类 Prototype 是用不到的。因为 .NET 在 System 命名空间中提供了 ICloneable 接口,其中就有唯一的方法 Clone() ,这样只需要实现这个接口就可以完成原型模式了。

 

简历的原型模式实现:

代码结构图:

3

 

   1: //简历
   2: class Resume : ICloneable
   3: {
   4:     private string name;
   5:     private string sex;
   6:     private string age;
   7:     private string timeArea;
   8:     private string company;
   9:  
  10:     public Resume(string name)
  11:     {
  12:         this.name = name;
  13:     }
  14:  
  15:     //设置个人信息
  16:     public void SetPersonalInfo(string sex, string age)
  17:     {
  18:         this.sex = sex;
  19:         this.age = age;
  20:     }
  21:     //设置工作经历
  22:     public void SetWorkExperience(string timeArea, string company)
  23:     {
  24:         this.timeArea = timeArea;
  25:         this.company = company;
  26:     }
  27:  
  28:     //显示
  29:     public void Display()
  30:     {
  31:         Console.WriteLine("{0} {1} {2}", name, sex, age);
  32:         Console.WriteLine("工作经历:{0} {1}", timeArea, company);
  33:     }
  34:  
  35:     public Object Clone()
  36:     {
  37:         return (Object)this.MemberwiseClone();
  38:     }
  39:  
  40: }

 

客户端实现,这里不同简历就可以做适当修改了:

   1: static void Main(string[] args)
   2: {
   3:     Resume a = new Resume("大鸟");
   4:     a.SetPersonalInfo("男", "29");
   5:     a.SetWorkExperience("1998-2000", "XX公司");
   6:  
   7:     Resume b = (Resume)a.Clone();
   8:     b.SetWorkExperience("1998-2006", "YY企业");
   9:  
  10:     Resume c = (Resume)a.Clone();
  11:     c.SetPersonalInfo("男", "24");
  12:  
  13:  
  14:     a.Display();
  15:     b.Display();
  16:     c.Display();
  17:  
  18:  
  19:     Console.Read();
  20: }

 

运行结果:

4

 

这样一来,客户端代码就清爽多了,而且如果想修改某一份简历,只需要对这份简历做一定的修改就行了,不会影响到其他简历,相同的部分就不用重复了。并且性能也大大提高了,一般在初始化的信息不发生变化的情况下,克隆是最好的办法,这既隐藏了对象创建的细节,又对大大提高了性能。

浅复制和深复制:

简历对象中的数据都是 string 型的,而 string 型是一种拥有值类型特点的特殊引用类型, MemberwiseClone() 方法对于字段是值类型的,则对字段执行逐位复制,如果字段是引用类型,则复制引用但不复制引用的对象。也就是说,如果简历当中有引用类型,那么引用对象的数据是不会被克隆过来的。

在一般设计中,针对简历通常会有一个“工作经历”类,当中有“工作时间”和“工作单位”等属性,“简历”类直接调用这个对象即可,这里的“工作经历”类就是引用类型。

代码结构图:

5

 

“工作经历”类:

   1: class WorkExperience
   2: {
   3:     private string workDate;
   4:     public string WorkDate
   5:     {
   6:         get { return workDate; }
   7:         set { workDate = value; }
   8:     }
   9:     private string company;
  10:     public string Company
  11:     {
  12:         get { return company; }
  13:         set { company = value; }
  14:     }
  15: }

 

让“简历”类直接调用这个对象:

   1: class Resume : ICloneable
   2: {
   3:     private string name;
   4:     private string sex;
   5:     private string age;
   6:  
   7:     private WorkExperience work;
   8:  
   9:     public Resume(string name)
  10:     {
  11:         this.name = name;
  12:         work = new WorkExperience();
  13:     }
  14:  
  15:     //设置个人信息
  16:     public void SetPersonalInfo(string sex, string age)
  17:     {
  18:         this.sex = sex;
  19:         this.age = age;
  20:     }
  21:     //设置工作经历
  22:     public void SetWorkExperience(string workDate, string company)
  23:     {
  24:         work.WorkDate = workDate;
  25:         work.Company = company;
  26:     }
  27:  
  28:     //显示
  29:     public void Display()
  30:     {
  31:         Console.WriteLine("{0} {1} {2}", name, sex, age);
  32:         Console.WriteLine("工作经历:{0} {1}", work.WorkDate, work.Company);
  33:     }
  34:  
  35:     public Object Clone()
  36:     {
  37:         return (Object)this.MemberwiseClone();
  38:     }
  39: }

 

客户端代码:

   1: static void Main(string[] args)
   2: {
   3:     Resume a = new Resume("大鸟");
   4:     a.SetPersonalInfo("男", "29");
   5:     a.SetWorkExperience("1998-2000", "XX公司");
   6:  
   7:     Resume b = (Resume)a.Clone();
   8:     b.SetWorkExperience("1998-2006", "YY企业");
   9:  
  10:     Resume c = (Resume)a.Clone();
  11:     c.SetPersonalInfo("男", "24");
  12:     c.SetWorkExperience("1998-2003", "ZZ企业");
  13:  
  14:     a.Display();
  15:     b.Display();
  16:     c.Display();
  17:  
  18:     Console.Read();
  19: }

 

运行结果:

6

 

MemberwiseClone() 由于是浅表引用,对于值类型没有什么问题,但对于引用类型就只是复制了引用,对引用的对象还是指向了原来的对象,所以看到了我们给 a、b、c 三个引用设置“工作经验”,但却同时看到三个引用都是最后一次设置,因为三个引用都指向了同一个对象。此时,解决这个问题就用到了深复制,它把引用对象的变量指向复制过的新对象,而不是原来被引用的对象。                      

 

简历的深复制实现

代码结构图:

7

 

“工作经历”类:

   1: class WorkExperience : ICloneable
   2: {
   3:     private string workDate;
   4:     public string WorkDate
   5:     {
   6:         get { return workDate; }
   7:         set { workDate = value; }
   8:     }
   9:     private string company;
  10:     public string Company
  11:     {
  12:         get { return company; }
  13:         set { company = value; }
  14:     }
  15:  
  16:     public Object Clone()
  17:     {
  18:         return (Object)this.MemberwiseClone();
  19:     }
  20: }

 

“简历”类:

   1: class Resume : ICloneable
   2: {
   3:     private string name;
   4:     private string sex;
   5:     private string age;
   6:  
   7:     private WorkExperience work;
   8:  
   9:     public Resume(string name)
  10:     {
  11:         this.name = name;
  12:         work = new WorkExperience();
  13:     }
  14:  
  15:     private Resume(WorkExperience work)
  16:     {
  17:         this.work = (WorkExperience)work.Clone();
  18:     }
  19:  
  20:     //设置个人信息
  21:     public void SetPersonalInfo(string sex, string age)
  22:     {
  23:         this.sex = sex;
  24:         this.age = age;
  25:     }
  26:     //设置工作经历
  27:     public void SetWorkExperience(string workDate, string company)
  28:     {
  29:         work.WorkDate = workDate;
  30:         work.Company = company;
  31:     }
  32:  
  33:     //显示
  34:     public void Display()
  35:     {
  36:         Console.WriteLine("{0} {1} {2}", name, sex, age);
  37:         Console.WriteLine("工作经历:{0} {1}", work.WorkDate, work.Company);
  38:     }
  39:  
  40:     public Object Clone()
  41:     {
  42:         Resume obj = new Resume(this.work);
  43:  
  44:         obj.name = this.name;
  45:         obj.sex = this.sex;
  46:         obj.age = this.age;
  47:  
  48:  
  49:         return obj;
  50:     }
  51:  
  52: }

 

客户端代码同上:

   1: static void Main(string[] args)
   2: {
   3:     Resume a = new Resume("大鸟");
   4:     a.SetPersonalInfo("男", "29");
   5:     a.SetWorkExperience("1998-2000", "XX公司");
   6:  
   7:     Resume b = (Resume)a.Clone();
   8:     b.SetWorkExperience("1998-2006", "YY企业");
   9:  
  10:     Resume c = (Resume)a.Clone();
  11:     c.SetWorkExperience("1998-2003", "ZZ企业");
  12:  
  13:     a.Display();
  14:     b.Display();
  15:     c.Display();
  16:  
  17:     Console.Read();
  18: }

 

运行结果:

8

 

在一些特定场合也长长设计浅复制和深复制,比如说,数据集对象 DataSet ,他就有 Clone() 方法和 Copy() 方法,Clone() 方法用来复制 DataSet 的结构,但不复制 DataSet 的数据,实现了原型模式的浅复制。 Copy() 方法不但复制结构,也复制数据,实现了深复制。

0 0
原创粉丝点击