我也设计模式——5.Prototype
我很喜欢这个模式,因为它解决了对象赋值的问题。尤其是那个Clone方法,记忆犹新。
使用场景:当一个系统应该独立于产品的创建,构成和表示时,使用设计模式。
.NET提供了ICloneable接口,只要实现了其Clone方法,在其中执行复制对象的操作。关键就是这个Clone方法,由于对象的复杂性,分为浅复制
和深复制两种:
对于浅复制,可以使用Object的MembermiseClone方法:
——这里说的不一样,不是具有相同字段/属性值的两个不同指针,我只是想找出两个从外表看有区别的对象。
对于深复制,要使用序列化来实现Clone方法,从而不仅仅复制对象本身,同时连同被引用的对象一起复制,代码如下:
这里只是说明如何正确使用串行化,而效果并不明显,如果引用一个数组,则能表现出深浅序列化的区别。
深复制在互相引用时,会陷入死循环:A引用B,B又引用A——对于此,是没有完美的解决方案的,为此,我们可以加一个didClone标记,以减少这种问题。
案例分析:
IDE中的ToolBox中各个小工具的实现,比如说Button和TextBox等,可以用原型模式实现。每个Control都要实现Clone()方法,比如说Label:
案例2,调色板,这个案例参见http://terrylee.cnblogs.com/archive/2006/01/16/317896.html,其思想与案例1是一样的:每次得到的色笔都是一个copy,仅仅是颜色的不同。由于每种颜色由RGB三原色组合搭配而成,所以会有256^3个对象,为此,使用原型模式可以将这个对象减少为1个,可以看成是水彩画中的一支毛笔和RGB三个色盒。
原型模式可以解决抽象工厂平行子类太多的问题,同样的,Flyweight也可以实现,只是后者更倾向于结构而不关心生成,这时候要使用注册工厂。
对于DataSet ds:
ds.Copy()方法,返回ds的结构和数据;
ds.Clone()方法,返回ds的结构
原型模式的缺点:
Prototype模式的最主要缺点就是每一个类必须配备一个克隆方法。而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事。
关于深浅复制的定义:
浅拷贝是指当对象的字段值被拷贝时,字段引用的对象不会被拷贝。例如,如果一个对象有一个指向字符串的字段,并且我们对该对象做了一个浅拷贝,那么两个对象将引用同一个字符串。而深拷贝是对对象实例中字段引用的对象也进行拷贝的一种方式,所以如果一个对象有一个指向字符串的字段,并且我们对该对象做了一个深拷贝的话,我们将创建一个新的对象和一个新的字符串--新对象将引用新字符串。需要注意的是执行深拷贝后,原来的对象和新创建的对象不会共享任何东西;改变一个对象对另外一个对象没有任何影响。
——以上摘自CLR框架设计,Array是最好的例证。
使用场景:当一个系统应该独立于产品的创建,构成和表示时,使用设计模式。
.NET提供了ICloneable接口,只要实现了其Clone方法,在其中执行复制对象的操作。关键就是这个Clone方法,由于对象的复杂性,分为浅复制
和深复制两种:
对于浅复制,可以使用Object的MembermiseClone方法:
public class A : ICloneable
{
public object Clone()
{
return this.MemberwiseClone();
}
}
但是,这样复制的对象没有任何区别。于是,可以使用如下折中的办法,{
public object Clone()
{
return this.MemberwiseClone();
}
}
class Program
{
static void Main()
{
A a1 = new A("bjq");
A a2 = (A)a1.Clone();
a2.f1 = "jax";
}
}
public class A : ICloneable
{
public string f1;
public string f2;
public A(string f1)
{
this.f1 = f1;
}
public object Clone()
{
A a = new A();
a.f2 = f2;
return a;
}
}
可以看到,对象a2虽然是a1的复制,但f1字段是不一样的。{
static void Main()
{
A a1 = new A("bjq");
A a2 = (A)a1.Clone();
a2.f1 = "jax";
}
}
public class A : ICloneable
{
public string f1;
public string f2;
public A(string f1)
{
this.f1 = f1;
}
public object Clone()
{
A a = new A();
a.f2 = f2;
return a;
}
}
——这里说的不一样,不是具有相同字段/属性值的两个不同指针,我只是想找出两个从外表看有区别的对象。
对于深复制,要使用序列化来实现Clone方法,从而不仅仅复制对象本身,同时连同被引用的对象一起复制,代码如下:
[Serializable]
public class A
{
private string f1;
private string f2;
public A(string f1, string f2)
{
this.f1 = f1;
this.f2 = f2;
}
public A Clone()
{
MemoryStream memoryStream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(memoryStream, this);
memoryStream.Position = 0;
A a = (A)formatter.Deserialize(memoryStream);
return a;
}
}
public class A
{
private string f1;
private string f2;
public A(string f1, string f2)
{
this.f1 = f1;
this.f2 = f2;
}
public A Clone()
{
MemoryStream memoryStream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(memoryStream, this);
memoryStream.Position = 0;
A a = (A)formatter.Deserialize(memoryStream);
return a;
}
}
这里只是说明如何正确使用串行化,而效果并不明显,如果引用一个数组,则能表现出深浅序列化的区别。
深复制在互相引用时,会陷入死循环:A引用B,B又引用A——对于此,是没有完美的解决方案的,为此,我们可以加一个didClone标记,以减少这种问题。
案例分析:
IDE中的ToolBox中各个小工具的实现,比如说Button和TextBox等,可以用原型模式实现。每个Control都要实现Clone()方法,比如说Label:
public class myLabel : Label, ICloneable
{
public object Clone()
{
myLabel ml = new myLabel();
return ml;
}
}
于是,在Form中实现如下:{
public object Clone()
{
myLabel ml = new myLabel();
return ml;
}
}
public partial class Form1 : Form
{
private Hashtable ht = new Hashtable();
private string status = "Label";
private void Form1_Load(object sender, EventArgs e)
{
MyLabel ml = new MyLabel();
ml.Text = "Hello";
ht.Add("Label", ml);
}
private void button1_Click(object sender, EventArgs e)
{
ICloneable fc = (ICloneable)ht[status];
Control c = (Control)fc.Clone();
this.Controls.Add(c);
}
}
我们用 “点击button1按钮” 来模拟 “从ToolBox中拖动Control到Form” 这个功能。这里仅仅实现了Label,我们还可以用同样的方法模拟Button,TreeView等控件。{
private Hashtable ht = new Hashtable();
private string status = "Label";
private void Form1_Load(object sender, EventArgs e)
{
MyLabel ml = new MyLabel();
ml.Text = "Hello";
ht.Add("Label", ml);
}
private void button1_Click(object sender, EventArgs e)
{
ICloneable fc = (ICloneable)ht[status];
Control c = (Control)fc.Clone();
this.Controls.Add(c);
}
}
案例2,调色板,这个案例参见http://terrylee.cnblogs.com/archive/2006/01/16/317896.html,其思想与案例1是一样的:每次得到的色笔都是一个copy,仅仅是颜色的不同。由于每种颜色由RGB三原色组合搭配而成,所以会有256^3个对象,为此,使用原型模式可以将这个对象减少为1个,可以看成是水彩画中的一支毛笔和RGB三个色盒。
原型模式可以解决抽象工厂平行子类太多的问题,同样的,Flyweight也可以实现,只是后者更倾向于结构而不关心生成,这时候要使用注册工厂。
对于DataSet ds:
ds.Copy()方法,返回ds的结构和数据;
ds.Clone()方法,返回ds的结构
原型模式的缺点:
Prototype模式的最主要缺点就是每一个类必须配备一个克隆方法。而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事。
关于深浅复制的定义:
浅拷贝是指当对象的字段值被拷贝时,字段引用的对象不会被拷贝。例如,如果一个对象有一个指向字符串的字段,并且我们对该对象做了一个浅拷贝,那么两个对象将引用同一个字符串。而深拷贝是对对象实例中字段引用的对象也进行拷贝的一种方式,所以如果一个对象有一个指向字符串的字段,并且我们对该对象做了一个深拷贝的话,我们将创建一个新的对象和一个新的字符串--新对象将引用新字符串。需要注意的是执行深拷贝后,原来的对象和新创建的对象不会共享任何东西;改变一个对象对另外一个对象没有任何影响。
——以上摘自CLR框架设计,Array是最好的例证。