大话设计模式之原型模式

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象

 

 

 

原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节,基本代码如下:

 

//原型类
abstractclass Prototype
{
         private string id;
         public Prototype(string id)
         {
                   this.id = id;
         }
 
         public string Id()
         {
                   get{ retrun id; }
         }
 
                                                                                             //抽象类关键就是有这样一个Clone方法
         public abstract Prototype Clone(); 
};
 
//具体原型类
classConcretPrototype1 :Prototype
{
         public ConcretPrototype1(string id) :base(id)
         {
         }
 
         public override Prototype Clone()
         {
                                                                                             //https://msdn.microsoft.com/zh-cn/library/system.object.memberwiseclone.aspx
                                                                                             //MemberwiseClone方法创建一个浅表副本,方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。
                                                                                             //如果字段是值类型的,则对该字段执行逐位复制。 如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。
                   return (Prototype)this.MemberwiseClone();
         }
 
};
 
//客户端代码
voidmain()
{
         ConcretPrototype1 p1 = new ConcretPrototype1("I");
         ConcretPrototype1 c1 = (ConcretPrototype1)p1.Clone();                //克隆类 ConcretPrototype1 的对象p1就能得到新的实例c1
}


 

 

其实,原型抽象类Prototype是用不着的。

因为,克隆实在是太常用了,.Net提供了ICloneable接口,其中唯一的方法就是Clone(), 这样只要实现这个接口就可以完成原型模式了

 

 

 

下面给出一个实例:

 

原题是需要给出几份相同的简历,但是我觉得有些牵强,于是就把题目改为生成几额类似的矩形,可以有细节上的差异。(虽然我把原题的代码结构图贴上来了)

 

 

 

classRectangle :ICloneable
{
         private int length;
         private int width;
         private string color;
         private string birthtime;
         private string author;
 
         public Rectangle(string author)
         {
                   this.author = author;
         }
 
         //detail infomation
         public void SetTangleInfo(int length, intwidth, string color)
         {
                   this.length = length;
                   this.width = width;
                   this.color = color;
         }
 
         //set create time
         public void SetTime(string birthtime)
         {
                   this.birthtime = birthtime;
         }
 
         //show
         public void Display()
         {
                   Console.WriteLine("{0}{1} {2}", length, width, color);
                   Console.WriteLine("CreateTime : {0}, Author : {1} ", birthtime, author);
         }
 
         public Object Clone
         {
                   return (Object)this.MemberwiseClone();
         }
};
 
//客户端
voidmain()
{
         Rectangle a = new Rectangle("Ben");
         a.SetTangleInfo(10,20,"red");
         a.SetTime("201504091049");
 
                                                                                    //只需要调用Clone方法就可以实现新矩形的生成,并且可以修改新矩形的细节
         Rectangle b = (Rectangle)a.Clone();
         b.SetTime("201504091050");
 
         Rectangle c = (Rectangle)a.Clone();
         c.SetTangleInfo(20,10,"blue");
 
         a.Display();
         b.Display();
         c.Display();
}


 

 

这样一来,如果每个细节都一样,那么我们的代码可以显得十分清爽。

性能也得到了提高,比如看对象a的初始化,假设是复杂的项目,如果每个对象都这样,那么就太低效了。

一般在初始化信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,又对性能是大大的提高。

相当于是不用重新初始化对象,而是动态的获得对象运行时的状态。

 

 

 

但是,我们也发现,我们使用的MemberwiseClone() 函数,根据描述是浅拷贝,这里再把那段话拿过来看看:

 

【https://msdn.microsoft.com/zh-cn/library/system.object.memberwiseclone.aspx

MemberwiseClone方法创建一个浅表副本,方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。

如果字段是值类型的,则对该字段执行逐位复制。 如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。】

 

当前矩形类中的数据都是值类型(int,string),所以没有什么问题,但是如果类中有对象引用,那么引用的对象数据是不会被克隆过来的。

 

接下来我们修改一下矩形类,让其中的时间类型不再是string类型而是一个类。那么在使用的时候就需要注意了。

下面就给出实现了深克隆的代码: (关于仅仅使用浅克隆的结果想必很容易想到,所以就不贴上来了)

不过这里我依旧把简历的类结构图贴上来:

 

 

 

classBirthTime :ICloneable
{
         private int year;
         public int Year
         {
                   get{ return year; }
                   set{ year = value; }
         }
 
         private int month;
         public int Month
         {
                   get{ return month; }
                   set{ month = value; }
         }
 
         private int day;
         public int Day
         {
                   get{ return day; }
                   set{ day = value; }
         }
 
         public Object Clone()                                                                         //时间类实现Clone方法
         {
                   return (Object)this.MemberwiseClone();
         }
};
 
classRectangle :ICloneable
{
         private int length;
         private int width;
         private string color;
         private BirthTime bt;
         private string author;
 
         public Rectangle(string author)
         {
                   this.author = author;
                   bt = new BirthTime();
         }
 
         public Rectangle(BirthTime bt)
         {                                                                                                                                 //这个构造函数是为了将来深克隆用的
                   this.bt = (BirthTime)bt.Clone();                                             //这里我们虽然是浅克隆,但是我们这里也只需要浅克隆就行了,因为该类里全是值类型而没有引用的
         }
 
         //detail infomation
         public void SetTangleInfo(int length, intwidth, string color)
         {
                   this.length = length;
                   this.width = width;
                   this.color = color;
         }
 
         //set create time
         public void SetTime(int year, int month,int day)
         {
                   bt.Year = year;
                   bt.Month = month;
                   bt.Day = day;
         }
 
         //show
         public void Display()
         {
                   Console.WriteLine("{0}{1} {2}", length, width, color);
                   Console.WriteLine("CreateTime : {0} {1} {2}, Author : {3} ", bt.Year, bt.Month, bt.Day, author);
         }
 
         public Object Clone()
         {
                   Rectangle obj = new Rectangle(this.bt);                            //这里就用到了之前准备好的构造函数,能这么用也依赖于BirthTime类中没有引用且实现的浅克隆(如果BirthTime中有引用,那么就需要一层一层的浅克隆来实现接下来一层的深克隆了)
                   obj.length = this.length;
                   obj.width = this.width;
                   obj.color = this.color;
                   obj.author = this.author;
                   return obj;                                                                                            //最终返回一个深复制的矩形对象
         }
};
 
//客户端
voidmain()
{
         Rectangle a = new Rectangle("Ben");
         a.SetTangleInfo(10, 20, "red");
         a.SetTime(2015,4,9);
 
         //只需要调用Clone方法就可以实现新矩形的生成,并且可以修改新矩形的细节
         Rectangle b = (Rectangle)a.Clone();
         b.SetTime(2015,4,10);
 
         Rectangle c = (Rectangle)a.Clone();
         c.SetTangleInfo(20, 10, "blue");
 
         a.Display();
         b.Display();
         c.Display();
}


 

这样,我们的输出结果就不再像浅克隆一样,a和b的时间类指向同一个时间对象,导致输出相同的时间了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值