深入详解原型模式之深复制和浅复制

了解GOF写的设计模式中的原型模式都知道其实它讲的就是对象的克隆(Clone).

《设计模式》里写道:原型模式的意图是:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.

其实它讲的就是深度复制,即复制一个现有的对象,改变复制后的属性或字段不影响模型对象.废话不多讲我们直接用一个简单的例子来看看这倒底是个神马意思.我们用一个例子来讲解.

假如现在要你打印出班级学生的所有详细信息.

假设学生信息只有学号、姓名、班级.

我们先打印一个学生的成绩.代码如下:

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Student s1
= new Student("001", "张三", "高三(3)班");
s1.Print();
}

}
class Student
{
private string _sno;
private string _name;
private string _classname;
public string Sno
{
get { return _sno; }
set { _sno = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
public string Classname
{
get { return _classname; }
set { _classname = value; }
}
public Student()
{
}
public Student(string sno, string name, string classname)
{
Sno
= sno;
Name
= name;
Classname
= classname;
}
public void Print()
{
Console.WriteLine(
"{0} {1} {2}", Sno, Name, Classname);
}
}
}

结果:

那么打印多个学生的话那么可以直接在Main函数里实例化学生即:

static void Main(string[] args)
{
Student s1
= new Student("001", "张三", "高三(3)班");
s1.Print();
Student s2
= new Student("002", "李四", "高三(3)班");
s2.Print();
.
.
.
}

但是现在要你动态的复制(Clone)生成对象的话那又该怎么写呢?代码如下

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Student s1
= new Student("001", "张三", "高三(3)班");
Student s2
= (Student)s1.Clone();
s2.Sno
= "002";
s2.Name
= "李四";
            s1.Print();

s2.Print();
}

}
class Student : ICloneable
{
private string _sno;
private string _name;
private string _classname;
public string Sno
{
get { return _sno; }
set { _sno = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
public string Classname
{
get { return _classname; }
set { _classname = value; }
}
        public Student()
        {
        }

public Student(string sno, string name, string classname)
{
Sno
= sno;
Name
= name;
Classname
=classname;
}
public Object Clone()
{
return this.MemberwiseClone();
}
public void Print()
{
Console.WriteLine(
"{0} {1} {2}", Sno,Name,Classname);
}
}
}

结果:

可能有同学要问这个this.MemberwiseClone()是个什么方法:其实他就相当于

public Object Clone()
{
//return this.MemberwiseClone();
Student obj = new Student();
obj.Sno
= this.Sno;
obj.Name
= this.Name;
obj.Classname
= this.Classname;
return obj;
}

到这里好像是复制一个完全独立的新对象,跟我们要谈的深复制和浅负责没多大关系.哈哈,那现在转折点要来了.浅复制在不经意间出现了

如果学生类里面加上性别年龄升高并且这三个属性用Feature类来封装.看看结果又会变成什么样 代码如下:

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Student s1
= new Student("001", "张三", "高三(3)班");
s1.SetFeature(
"", "176厘米", "18");
Student s2
= (Student)s1.Clone();
s2.Sno
= "002";
s2.Name
= "李四";
s2.SetFeature(
"", "180厘米", "19");
s1.Print();
s2.Print();
}

}
class Student : ICloneable
{
private string _sno;
private string _name;
private string _classname;
private Feature _fe;
public string Sno
{
get { return _sno; }
set { _sno = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
public string Classname
{
get { return _classname; }
set { _classname = value; }
}
public Student()
{
_fe
= new Feature();
}
public Student(string sno, string name, string classname)
{
Sno
= sno;
Name
= name;
Classname
= classname;
_fe
= new Feature();
}
public void SetFeature(string sex,string high,string age)
{
_fe.Sex
= sex;
_fe.High
= high;
_fe.Age
= age;
}
public Object Clone()
{
return this.MemberwiseClone();
}
public void Print()
{
Console.WriteLine(
"{0} {1} {2}", Sno, Name, Classname);
Console.WriteLine(
"{0} {1} {2}", _fe.Sex, _fe.High, _fe.Age);
}
}
class Feature
{
private string _sex;
private string _age;
private string _high;
public string Sex
{
get { return _sex; }
set { _sex = value; }
}
public string High
{
get { return _high; }
set { _high = value; }
}
public string Age
{
get { return _age; }
set { _age = value; }
}
public Feature()
{
}
public Feature(string sex, string high, string age)
{
Sex
= sex;
High
= high;
Age
= age;
}
}
}

出现了我们意想不到的结果:  (这就是所谓的浅复制,改变复制后的实例对象属性会影响到原型的属性值变化)   

我们期望的是出现这种结果:   (这就是我们期望的深复制,改变复制后的实例对象不会影响到原型的属性值)

为什么会出现这种情况呢,其实我把 public Object Clone()函数换一种写法大家就会明白问题出现在哪里了 public Object Clone()等实现方法如下:

public Object Clone()
{
//return this.MemberwiseClone();
Student obj = new Student();
obj.Sno
= this.Sno;
obj.Name
= this.Name;
obj.Classname
= this.Classname;
obj._fe
= this._fe;
return obj;
}

眼尖的同学一眼就发现了问题所在!没错,问题就出现在 obj._fe=this._fe;这句代码身上;

对象是引用类型,这句代码的意思是复制后的s2对象和原型对象s1共用一个_fe,导致s2修改了_fe里面的属性值.原型对象s1的_fe也跟着变.

系统调用 return this.MenberwiseClone()方法就是上面的那些具体代码,这样产生了浅复制.其实想复制的话代码应该是:

public Object Clone()
{
//return this.MemberwiseClone();
Student obj = new Student();
obj.Sno
= this.Sno;
obj.Name
= this.Name;
obj.Classname
= this.Classname;
//obj._fe = this._fe;
obj._fe.Age = this._fe.Age;
obj._fe.High
= this._fe.High;
obj._fe.Sex
= this._fe.Sex;
return obj;
}

这样的话就是深复制就是我们期望的结果.

其实       

obj._fe.Age = this._fe.Age;
obj._fe.High = this._fe.High;
obj._fe.Sex = this._fe.Sex;

这段代码是实例化一个Feature 对象在内在分配一个对象空间.好让复制后的S1和S2对象不共享同一个Feature对象而已.如果我们还希望能用到this.MenberwiseClone()方法那么该怎么改写代码呢?

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Student s1
= new Student("001", "张三", "高三(3)班");
s1.SetFeature(
"", "176厘米", "18");
Student s2
= (Student)s1.Clone();
s2.Sno
= "002";
s2.Name
= "李四";
s2.SetFeature(
"", "180厘米", "19");
s1.Print();
s2.Print();
}

}
class Student : ICloneable
{
private string _sno;
private string _name;
private string _classname;
private Feature _fe;
public string Sno
{
get { return _sno; }
set { _sno = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
public string Classname
{
get { return _classname; }
set { _classname = value; }
}
public Student(Feature fe)
{
_fe
= (Feature)fe.Clone();
}
public Student()
{
_fe
= new Feature();
}
public Student(string sno, string name, string classname)
{
Sno
= sno;
Name
= name;
Classname
= classname;
_fe
= new Feature();
}
public void SetFeature(string sex,string high,string age)
{
_fe.Sex
= sex;
_fe.High
= high;
_fe.Age
= age;
}
public Object Clone()
{
Student obj
= new Student(this._fe);
obj.Sno
= this.Sno;
obj.Name
= this.Name;
obj.Classname
= this.Classname;
//obj._fe = this._fe;
//obj._fe.Age = this._fe.Age;
//obj._fe.High = this._fe.High;
//obj._fe.Sex = this._fe.Sex;
return obj;
}
public void Print()
{
Console.WriteLine(
"{0} {1} {2}", Sno, Name, Classname);
Console.WriteLine(
"{0} {1} {2}", _fe.Sex, _fe.High, _fe.Age);
}
}
class Feature :ICloneable
{
private string _sex;
private string _age;
private string _high;
public string Sex
{
get { return _sex; }
set { _sex = value; }
}
public string High
{
get { return _high; }
set { _high = value; }
}
public string Age
{
get { return _age; }
set { _age = value; }
}
public Feature()
{
}
public Feature(string sex, string high, string age)
{
Sex
= sex;
High
= high;
Age
= age;
}
public Object Clone()
{
return this.MemberwiseClone();
}
}
}

结果就是我们期望的:

所以要深复制的话且用到MenberwiseClone()方法的话,要保证使用MenberwiseClone()方法的对象类是没用引用类型属性(即没有类对象属性)

上面完整的代码就是著名的设计模式里的原型模式.如果你搞不懂深复制和浅复制也就淡不上搞懂原型模式了

给大家留一个思考 如果Feature  里面再包含一个兴趣类即Interest类里面包含三个兴趣属性 那么该如何深拷贝Student对象呢(也就是怎么实现原型模式)且用到MenberwiseClone()方法当然了也可以不用到.

class Feature :ICloneable
{
private string _sex;
private string _age;
private string _high;
private Interest _interest;
}
class Interest
{
private string _it1;
private string _it2;
private string _it3;
public string It1
{
get { return _it1; }
set { _it1 = value; }
}
public string It2
{
get { return _it2; }
set { _it2 = value; }
}
public string It3
{
get { return _it3; }
set { _it3 = value; }
}
public Interest()
{
}
public Interest(string it1,string it2,string it3)
{
It1
= it1;
It2
= it2;
It3
= it3;
}
}
posted @ 2011-07-02 01:16  隆中小屋  阅读(934)  评论(1编辑  收藏  举报