设计模式-创建型-原型模式
引言:
原型模式是什么?它是在什么场景下被提出的呢?本章节,我们将详细了解下原型模式。
在软件系统中,当创建一个类的实例过程过于昂贵或复杂,并且我们需要创建多个这样类的实例时,如果我们通过new来创建类实例,这就会增加创建类的复杂度和创建过程与客户代码复杂的耦合度。如果采用工厂模式来创建这样的实例对象的话,随着产品类的不断增加,导致子类的数量不断增多,也导致了相应工厂类的增加,维护的代码维度增加了,因为有产品和工厂两个维度了,反而增加了系统复杂程度,所以在这里使用工厂模式来封装类创建过程并不合适。由于每个类实例都是相同的(类型相同),但是每个实例的状态参数会有不同,如果状态数值也相同就没意义了,有一个这样的对象就可以了。当我们需要多个相同的类实例时,可以通过对原来对象拷贝一份来完成创建,这个思路正是原型模式的实现方式。
定义:
原型模式就是通过给出一个原型对象来指明所要创建的对象类型,然后用复制这个对象的方法来创建更多的同类型对象。
原型模式的两种类型:
object类的clone方法只会拷贝对象中基本的数据类型,对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。
1 internal class program 2 { 3 private static void main(string[] args) 4 { 5 sunwukong sunwukong = new sunwukong() 6 { 7 id = 1, 8 name = "孙悟空", 9 weapon = new weapon() 10 { 11 name = "如意金箍棒" 12 } 13 }; 14 sunwukong xingzhesun = (sunwukong)sunwukong.clone(); 15 console.writeline($"{sunwukong.id}-{sunwukong.name}-{sunwukong.weapon.name}"); 16 console.writeline($"{xingzhesun.id}-{xingzhesun.name}-{xingzhesun.weapon.name}"); 17 console.writeline("==============================="); 18 19 // 验证克隆后属性之间是否共享 20 xingzhesun.id = 2; 21 xingzhesun.name = "行者孙"; 22 xingzhesun.weapon.name = "钉耙"; 23 console.writeline($"{xingzhesun.id}-{xingzhesun.name}-{xingzhesun.weapon.name}"); 24 console.writeline($"{sunwukong.id}-{sunwukong.name}-{sunwukong.weapon.name}"); 25 // 从结果可以看出,对于基本类型属性,克隆之后不共享,而对于对象来说是共享的,这就是浅拷贝 26 console.writeline("==============================="); 27 console.writeline($"孙悟空的武器{sunwukong.weapon.gethashcode()},行者孙的武器{sunwukong.weapon.gethashcode()}"); 28 // 打印hashcode值可以看出,克隆后的实例与原型的weapon指向同一个地址 29 } 30 } 31 32 internal class sunwukong : icloneable 33 { 34 public int id { get; set; } 35 public string name { get; set; } 36 public weapon weapon { get; set; } 37 38 public object clone() 39 { 40 return this.memberwiseclone(); 41 } 42 } 43 44 internal class weapon 45 { 46 public string name { get; set; } 47 }
.net中提供了原型,即system命名空间下的接口icloneable,只要实现该接口的clone方法就可以之间原型拷贝。上述代码中遗留了一个问题,如何实现深拷贝问题。
1 internal class program 2 { 3 private static void main(string[] args) 4 { 5 sunwukong sunwukong = new sunwukong() 6 { 7 id = 1, 8 name = "孙悟空", 9 weapon = new weapon() 10 { 11 name = "如意金箍棒" 12 } 13 }; 14 sunwukong xingzhesun = (sunwukong)sunwukong.clone(); 15 // 引用类型再进行拷贝 16 xingzhesun.weapon = (weapon)xingzhesun.weapon.clone(); 17 console.writeline($"{sunwukong.id}-{sunwukong.name}-{sunwukong.weapon.name}"); 18 console.writeline($"{xingzhesun.id}-{xingzhesun.name}-{xingzhesun.weapon.name}"); 19 console.writeline("==============================="); 20 xingzhesun.id = 2; 21 xingzhesun.name = "行者孙"; 22 xingzhesun.weapon.name = "钉耙"; 23 console.writeline($"{xingzhesun.id}-{xingzhesun.name}-{xingzhesun.weapon.name}"); 24 console.writeline($"{sunwukong.id}-{sunwukong.name}-{sunwukong.weapon.name}"); 25 console.writeline("==============================="); 26 console.writeline($"孙悟空的武器{sunwukong.weapon.gethashcode()},行者孙的武器{xingzhesun.weapon.gethashcode()}"); 27 } 28 } 29 30 internal class sunwukong : icloneable 31 { 32 public int id { get; set; } 33 public string name { get; set; } 34 public weapon weapon { get; set; } 35 36 public object clone() 37 { 38 return this.memberwiseclone(); 39 } 40 } 41 42 internal class weapon : icloneable 43 { 44 public string name { get; set; } 45 46 public object clone() 47 { 48 return this.memberwiseclone(); 49 } 50 }
深拷贝:
1、复制对象的基本数据类型的成员变量值。
2、为所有引用类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝。
3、深拷贝实现方式1:重写clone方法。
4、深拷贝实现方式2:通过对象序列化。
1 //对象深拷贝 2 public static t copy<t>(t oldobject) where t : class,new() 3 { 4 t neworder = new t(); 5 memorystream stream = new memorystream(); 6 binaryformatter bf = new binaryformatter(); 7 bf.serialize(stream, oldobject); 8 stream.position = 0; 9 neworder = (bf.deserialize(stream) as t); 10 return neworder; 11 }
原型模式的注意事项和细节:
1、创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率。
2、不用重新初始化对象,而是动态地获取对象运行时的状态。
3、如果原始对象发生变化(增加或减少属性),其它克隆对象也会发生相应的变化,无需修改代码。
4、在实现深克隆的时候可能需要比较复杂的代码。
5、使用原型模式复制不会调用类的构造方法。因为对象的复制是通过调用clone方法完成的,它直接在内存种复制数据,因此不会调用到类的构造方法。不但构造方法中的代码不会执行,甚至连访问权限都对原型模式无效。单例模式中,我们通过私有化构造函数来实现单例模式,但clone方法直接无视构造方法的权限,所以,单例模式与原型模式是冲突的。
原型模式的优点:
1、原型模式向客户隐藏了创建新实例的复杂性。
2、原型模式允许动态增加或较少产品类。
3、原型模式简化了实例的创建结构,工厂方法模式需要有一个与产品类等级结构相同的等级结构,而原型模式不需要这样。
4、产品类不需要事先确定产品的等级结构,因为原型模式适用于任何的等级结构。
缺点:
1、需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改源码,即违反了ocp原则。
参考:https://www.cnblogs.com/zhili/p/prototypepattern.html