经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
设计模式-原型模式详解 - Vincent-Huang
来源:cnblogs  作者:Vincent-Huang  时间:2019/8/21 9:34:48  对本文有异议

一、原型模式的概念

  原型模式属于创建型设计模式。当要创建的对象类型由原型实例确定时使用它,该实例被克隆以生成新对象。

  此模式用于

  1、避免客户端应用程序中的对象创建者的子类,如工厂方法模式。

  2、避免以标准方式创建新对象的固有成本(例如,使用'new'关键字),当它对于给定的应用程序来说过于昂贵时。

  上述定义来自wiki,简单来说,就是创建一个全新的对象代价太高时,比如耗时太长时,能直接从现有实例中以较小的代价克隆出一个对象。

二、原型模式的实现

  实现原型模式的方式之一为克隆,下面我们来实现一下

  1、先定义一个抽象原型,包含两个克隆的虚方法(方便子类重写)

  1. namespace PrototypePattern
  2. {
  3. using System;
  4. using System.IO;
  5. using System.Runtime.Serialization.Formatters.Binary;
  6. [Serializable]
  7. public abstract class AbstractPrototype
  8. {
  9. /// <summary>
  10. /// 浅克隆
  11. /// <remarks>使用objectMemberwiseClone</remarks>
  12. /// </summary>
  13. /// <returns>当前对象副本</returns>
  14. public virtual AbstractPrototype Clone()
  15. {
  16. return (AbstractPrototype)this.MemberwiseClone();
  17. }
  18. /// <summary>
  19. /// 深克隆
  20. /// <remarks>使用二进制序列化反序列化实现</remarks>
  21. /// </summary>
  22. /// <returns>当前对象副本</returns>
  23. public virtual AbstractPrototype DeepClone()
  24. {
  25. MemoryStream stream = new MemoryStream();
  26. BinaryFormatter binaryFormatter = new BinaryFormatter();
  27. binaryFormatter.Serialize(stream, this);
  28. stream.Position = 0;
  29. var instance = (AbstractPrototype)binaryFormatter.Deserialize(stream);
  30. stream.DisposeAsync();
  31. return instance;
  32. }
  33. }
  34. }

2、需要实现原型模式的类继承此抽象类即可

  1. namespace PrototypePattern
  2. {
  3. using System;
  4. [Serializable]
  5. public class ConcretePrototype : AbstractPrototype
  6. {
  7. public int Number { get; set; }
  8. public Person Person { get; set; }
  9. }
  10. }

Person类

  1. namespace PrototypePattern
  2. {
  3. using System;
  4. [Serializable]
  5. public class Person
  6. {
  7. public string Name { get; set; }
  8. public int Age { get; set; }
  9. }
  10. }

以上代码就实现了原型模式。

其中的克隆方式又分为浅克隆,深克隆,深克隆使用的是序列化反序列化的方式(需要在类上增加[Serializable]特性)。

浅克隆
复制出来的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。

深克隆
复制出来的所有变量都含有与原来的对象相同的值,那些引用其他对象的变量将指向复制出来的新对象,是一个全新的副本。

我们来测试一波

  1. {
  2. ConcretePrototype no0 = new ConcretePrototype() { Number = 0, Person = new Person() { Age = 17, Name = "Vincent" } };
  3. Console.WriteLine("第一次构造");
  4. Console.WriteLine($"No0:Number:{no0.Number},Age:{no0.Person.Age},Name:{no0.Person.Name}");
  5. Console.WriteLine("从No0浅克隆到No1");
  6. ConcretePrototype no1 = (ConcretePrototype)no0.Clone();
  7. Console.WriteLine("修改No1");
  8. no1.Person.Age = 18;
  9. no1.Person.Name = "Vincent1";
  10. Console.WriteLine($"No0:Number:{no0.Number},Age:{no0.Person.Age},Name:{no0.Person.Name}");
  11. Console.WriteLine($"No1:Number:{no1.Number},Age:{no1.Person.Age},Name:{no1.Person.Name}");
  12. Console.WriteLine("******************");
  13. Console.WriteLine("从No0浅克隆到No2");
  14. ConcretePrototype no2 = (ConcretePrototype)no0.DeepClone();
  15. Console.WriteLine("修改No2");
  16. no2.Person.Age = 19;
  17. no2.Person.Name = "Vincent2";
  18. Console.WriteLine($"No0:Number:{no0.Number},Age:{no0.Person.Age},Name:{no0.Person.Name}");
  19. Console.WriteLine($"No2:Number:{no2.Number},Age:{no2.Person.Age},Name:{no2.Person.Name}");
  20. Console.WriteLine("******************");
  21. }

看看结果

可以看到,通过浅克隆得到的对象和原对象共享引用对象,深克隆的反之。

以上是一般的实现方式,但是这里使用的是继承抽象原型类的方式。我们知道,C#里是单继承,我们这里继承了抽象的原型类,就不能继承其他类了,难免会有些不方便。下面我们来改进一下。

三、改进

  我们知道.NET框架里为我们提供了ICloneable接口,此接口只包含一个返回值为object的Clone方法。object类中提供了返回值为object、访问级别为protected的MemberwiseClone方法。那么我们只需要继承ICloneable接口,实现Clone方法,把Clone方法访问级别改为public即可实现原型模式,如下

  1. namespace PrototypePattern
  2. {
  3. using System;
  4. [Serializable]
  5. public class ConcretePrototype2 : ICloneable
  6. {
  7. public int Number { get; set; }
  8. public Person Person { get; set; }
  9. /// <summary>Creates a new object that is a copy of the current instance.</summary>
  10. /// <returns>A new object that is a copy of this instance.</returns>
  11. public object Clone()
  12. {
  13. return this.MemberwiseClone();
  14. }
  15. }
  16. }

这里只有浅克隆,没有深克隆,怎么办?我们使用扩展方法,为ICloneable接口扩展一个深克隆方法,如下

  1. namespace PrototypePattern
  2. {
  3. using System;
  4. using System.IO;
  5. using System.Runtime.Serialization.Formatters.Binary;
  6. public static class ICloneableExtension
  7. {
  8. public static T DeepClone<T>(this ICloneable instance)
  9. {
  10. MemoryStream stream = new MemoryStream();
  11. BinaryFormatter binaryFormatter = new BinaryFormatter();
  12. binaryFormatter.Serialize(stream, instance);
  13. stream.Position = 0;
  14. var duplicate = binaryFormatter.Deserialize(stream);
  15. stream.DisposeAsync();
  16. return (T)duplicate;
  17. }
  18. }
  19. }

 四、总结

  以上的实现只能应对一般的情况,如果需要应对复杂的情况,如克隆对象中指定的对象(例如单据的复制),就需要改进或增加克隆的实现方式。

  文中的代码下载:https://github.com/hzhhhbb/PrototypePattern

五、参考资料

  https://en.wikipedia.org/wiki/Prototype_pattern

原文链接:http://www.cnblogs.com/hzhhhbb/p/11386236.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号