设计模式(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: 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: }
运行结果同上。现在由于不同单位对于求职者的兴趣点不同,应对不同公司的简历也略有差别,该怎么办?
这就是原型模式啦,所谓的原型模式,定义是用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节。
原型模式结构图:
基本的原型模式代码:
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() ,这样只需要实现这个接口就可以完成原型模式了。
简历的原型模式实现:
代码结构图:
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: }
运行结果:
这样一来,客户端代码就清爽多了,而且如果想修改某一份简历,只需要对这份简历做一定的修改就行了,不会影响到其他简历,相同的部分就不用重复了。并且性能也大大提高了,一般在初始化的信息不发生变化的情况下,克隆是最好的办法,这既隐藏了对象创建的细节,又对大大提高了性能。
浅复制和深复制:
简历对象中的数据都是 string 型的,而 string 型是一种拥有值类型特点的特殊引用类型, MemberwiseClone() 方法对于字段是值类型的,则对字段执行逐位复制,如果字段是引用类型,则复制引用但不复制引用的对象。也就是说,如果简历当中有引用类型,那么引用对象的数据是不会被克隆过来的。
在一般设计中,针对简历通常会有一个“工作经历”类,当中有“工作时间”和“工作单位”等属性,“简历”类直接调用这个对象即可,这里的“工作经历”类就是引用类型。
代码结构图:
“工作经历”类:
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: }
运行结果:
MemberwiseClone() 由于是浅表引用,对于值类型没有什么问题,但对于引用类型就只是复制了引用,对引用的对象还是指向了原来的对象,所以看到了我们给 a、b、c 三个引用设置“工作经验”,但却同时看到三个引用都是最后一次设置,因为三个引用都指向了同一个对象。此时,解决这个问题就用到了深复制,它把引用对象的变量指向复制过的新对象,而不是原来被引用的对象。
简历的深复制实现
代码结构图:
“工作经历”类:
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: }
运行结果:
在一些特定场合也长长设计浅复制和深复制,比如说,数据集对象 DataSet ,他就有 Clone() 方法和 Copy() 方法,Clone() 方法用来复制 DataSet 的结构,但不复制 DataSet 的数据,实现了原型模式的浅复制。 Copy() 方法不但复制结构,也复制数据,实现了深复制。
- 设计模式:6)原型设计模式
- 设计模式(6):原型模式
- 设计模式(6)--原型模式
- 设计模式(6) ------- 原型模式
- 设计模式(6)-原型模式
- 设计模式-原型设计模式(Prototype)
- 设计模式-原型设计模式(Prototype)
- 设计模式:原型模式
- 设计模式------原型模式
- 设计模式 原型模式
- 设计模式-【原型模式】
- 设计模式-原型模式
- 设计模式-原型模式
- 设计模式-原型模式
- 设计模式-原型模式
- 设计模式- 原型模式
- 设计模式:原型模式
- 设计模式 - 原型模式
- SEAndroid安全机制对Android属性访问的保护分析
- 哇,还有工程模式
- mysql导入source数据库sql的C++实现和封装
- POJ 2063 Investment 变形的完全背包
- 无线传输杂叙三
- 设计模式(6):原型模式
- 昨天晚上斗了几场地主,得到一门挑战赛冠军,喜得10000。开发欢乐斗地主游戏辅助神器
- 黑马程序员-------集合(ArrayList,LinkedList)
- POJ 1384 Piggy-Bank 完全背包
- UVA 10954- Add All(优先队列)
- Hive 中内部表与外部表的区别与创建方法
- 读取七位数的代码片段
- ESql修改(介绍对数据库的增、删、改用法,Edoit的核心内容之一)
- 基于AWS的广告服务应用架构