• 原型模式(prototype pattern)


            原型模式其实很简单,又可以成为克隆模式。想象你要造一架飞机,如果你要重头到位掌握造飞机的核心技术,那么你要从力学,材料学开始学起。但是你想快点看到成果,那你就把对应的零件买回来自己组装一个就好了。

    目录

    1.UML图

    2. 深拷贝与浅拷贝

    3.案例

    4.小结

    4.1 优点

    4.2 缺点

     4.3 使用场景


    1.UML图

            

     可以看出,在原型模式的结构图有以下角色:
    (1)、原型类(Prototype):原型类,声明一个Clone自身的接口;
    (2)、具体原型类(ConcretePrototype):实现一个Clone自身的操作。
    在原型模式中,Prototype通常提供一个包含Clone方法的接口,具体的原型ConcretePrototype使用Clone方法完成对象的创建。

            所以关键在如何实现Clone()函数。

    2. 深拷贝与浅拷贝

             相比大家都知道深拷贝与浅拷贝的区别,这里不过多讲述了。只是在C#里面系统提供了一个函数:

    protected object MemberwiseClone ();

     我们先来讲解一下这个函数,再给出案例。这个函数是一个浅拷贝函数,该方法 MemberwiseClone 通过创建新对象,然后将当前对象的非静态字段复制到新对象来创建浅表副本。 如果字段是值类型,则执行字段的逐位副本。 如果字段是引用类型,则会复制引用,但引用对象不是;因此,原始对象及其克隆引用同一对象。

    官方的例子其实很典型:

    1. using System;
    2. public class IdInfo
    3. {
    4. public int IdNumber;
    5. public IdInfo(int IdNumber)
    6. {
    7. this.IdNumber = IdNumber;
    8. }
    9. }
    10. public class Person
    11. {
    12. public int Age;
    13. public string Name;
    14. public IdInfo IdInfo;
    15. public Person ShallowCopy()
    16. {
    17. return (Person) this.MemberwiseClone();
    18. }
    19. public Person DeepCopy()
    20. {
    21. Person other = (Person) this.MemberwiseClone();
    22. other.IdInfo = new IdInfo(IdInfo.IdNumber);
    23. other.Name = String.Copy(Name);
    24. return other;
    25. }
    26. }
    27. public class Example
    28. {
    29. public static void Main()
    30. {
    31. // Create an instance of Person and assign values to its fields.
    32. Person p1 = new Person();
    33. p1.Age = 42;
    34. p1.Name = "Sam";
    35. p1.IdInfo = new IdInfo(6565);
    36. // Perform a shallow copy of p1 and assign it to p2.
    37. Person p2 = p1.ShallowCopy();
    38. // Display values of p1, p2
    39. Console.WriteLine("Original values of p1 and p2:");
    40. Console.WriteLine(" p1 instance values: ");
    41. DisplayValues(p1);
    42. Console.WriteLine(" p2 instance values:");
    43. DisplayValues(p2);
    44. // Change the value of p1 properties and display the values of p1 and p2.
    45. p1.Age = 32;
    46. p1.Name = "Frank";
    47. p1.IdInfo.IdNumber = 7878;
    48. Console.WriteLine("\nValues of p1 and p2 after changes to p1:");
    49. Console.WriteLine(" p1 instance values: ");
    50. DisplayValues(p1);
    51. Console.WriteLine(" p2 instance values:");
    52. DisplayValues(p2);
    53. // Make a deep copy of p1 and assign it to p3.
    54. Person p3 = p1.DeepCopy();
    55. // Change the members of the p1 class to new values to show the deep copy.
    56. p1.Name = "George";
    57. p1.Age = 39;
    58. p1.IdInfo.IdNumber = 8641;
    59. Console.WriteLine("\nValues of p1 and p3 after changes to p1:");
    60. Console.WriteLine(" p1 instance values: ");
    61. DisplayValues(p1);
    62. Console.WriteLine(" p3 instance values:");
    63. DisplayValues(p3);
    64. }
    65. public static void DisplayValues(Person p)
    66. {
    67. Console.WriteLine(" Name: {0:s}, Age: {1:d}", p.Name, p.Age);
    68. Console.WriteLine(" Value: {0:d}", p.IdInfo.IdNumber);
    69. }
    70. }
    71. // The example displays the following output:
    72. // Original values of p1 and p2:
    73. // p1 instance values:
    74. // Name: Sam, Age: 42
    75. // Value: 6565
    76. // p2 instance values:
    77. // Name: Sam, Age: 42
    78. // Value: 6565
    79. //
    80. // Values of p1 and p2 after changes to p1:
    81. // p1 instance values:
    82. // Name: Frank, Age: 32
    83. // Value: 7878
    84. // p2 instance values:
    85. // Name: Sam, Age: 42
    86. // Value: 7878
    87. //
    88. // Values of p1 and p3 after changes to p1:
    89. // p1 instance values:
    90. // Name: George, Age: 39
    91. // Value: 8641
    92. // p3 instance values:
    93. // Name: Frank, Age: 32
    94. // Value: 7878

    IdInfo是一个引用类型,所以浅拷贝是拷贝的是它的引用,当原对象中的IdInfo改变时,浅拷贝对象也会改变。

            所以当你实现Clone函数时,要注意那些是值对象,那些是引用对象!

    3.案例

            这里我构造了一个Person抽象类:重点关注ShallowClone(),和DeepClone().

    1. internal abstract class Person
    2. {
    3. public string? Name { get; set; }
    4. public int? Age { get; set; }
    5. public Address? Address { get; set; }
    6. public abstract Person? ShallowClone();
    7. public abstract Person? DeepClone();
    8. }

    然后有一个具体的Custome类派生自person,并额外多了字段:

    1. internal class Custome:Person
    2. {
    3. public Order? Order { get; set; }
    4. public Custome(string name,Address address)
    5. {
    6. Name = name;
    7. Address = address;
    8. }
    9. public void SetOrder(Order order)=>Order = order;
    10. public override Custome? ShallowClone()
    11. {
    12. return this.MemberwiseClone() as Custome;
    13. }
    14. public override Custome? DeepClone()
    15. {
    16. Custome custome = (Custome)MemberwiseClone();
    17. custome.Address = new Address( Address?.Country ?? "China", Address?.City ?? "Beijing");
    18. custome.Order = Order?.Clone();
    19. return custome;
    20. }
    21. public void Show()
    22. {
    23. Console.WriteLine($"Name: {Name}");
    24. Console.WriteLine($"Age: {Age}");
    25. Console.WriteLine($"Address; {Address?.Country} ,{Address?.City}");
    26. Console.WriteLine($"Order: \n \t at: { Order?.Time} " +
    27. $"\t goods: {Order?.Goods.Aggregate((x, y) => x + " " + y)}");
    28. }
    29. }

    ShallowClone函数没啥好说的了,直接调用MemberWiseClone完事。重点看DeepClone函数。

    这里Adress和Order都是引用类型,其中Adresss是通过构造函数创建新对象,Order则不是,我们为Order实现了一个clone函数,专门用于创建对象。

    1. internal class Order
    2. {
    3. public Guid Guid { get; set; }
    4. public DateTime Time { get; set; }
    5. public List<string> Goods { get; set; } = new List<string>();
    6. public Order()
    7. {
    8. Guid = Guid.NewGuid();
    9. Time = DateTime.Now;
    10. }
    11. public Order? Clone()
    12. {
    13. //不能直接返回对象o,yinwei Goods是引用类型,修改Goods会影响到克隆对象里面
    14. var o= MemberwiseClone() as Order;
    15. o.Goods=Goods.ToList();
    16. return o;
    17. }
    18. public void AddGood(string item)=>Goods.Add(item);
    19. }

    要注意Goods是引用类型,所以不能直接调用MemberWiseClone

    Address就没啥特别了:

    1. internal class Address
    2. {
    3. public string Country { get; set; }
    4. public string City { get; set; }
    5. public Address(string country,string city)
    6. {
    7. Country = country;
    8. City = city;
    9. }
    10. }

    最后简单测试一下:

    1. Address address = new Address("美国", "纽约");
    2. Custome c1 = new Custome("川普", address);
    3. Order Order = new Order();
    4. Order.AddGood("Chip");
    5. Order.AddGood("Gun");
    6. c1.SetOrder(Order);
    7. c1.Show();
    8. var c2=c1.ShallowClone();
    9. var c3=c1.DeepClone();
    10. c2?.Show();
    11. c3?.Show();
    12. var o1 = Order.Clone();
    13. c1?.Order?.AddGood("软中华");
    14. c1.Age = 20;
    15. address.City = "华盛顿";
    16. c1.Show();
    17. c2?.Show();
    18. c3?.Show();

    4.小结

    4.1 优点

     (1)、原型模式向客户隐藏了创建新实例的复杂性
    (2)、原型模式允许动态增加或较少产品类。
    (3)、原型模式简化了实例的创建结构,工厂方法模式需要有一个与产品类等级结构相同的等级结构,而原型模式不需要这样。
    (4)、产品类不需要事先确定产品的等级结构,因为原型模式适用于任何的等级结构

    4.2 缺点

    (1)、每个类必须配备一个克隆方法
    (2)、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。 

     4.3 使用场景

     1)、资源优化场景
      类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
    (2)、性能和安全要求的场景
      通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
    (3)、一个对象多个修改者的场景
      一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
    在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。


    完整代码:PatternDesignReview

  • 相关阅读:
    【GB28181】wvp-GB28181-pro修改分屏监控为16画面(前端)
    docker root dir 迁移方案
    Cyanine7-NH2细胞成像1650559-73-1星戈瑞
    STM32 DMA从存储器发送数据到串口
    深度学习之路=====9=====>>MobileNet(tensorflow2)
    MyBatis的< resultMap >标签的简析
    ptyhon flask SSE 浏览器和服务器实时通信-例子实时推送随机数到前端画echart曲线图
    springboot配置https
    第一章《初学者问题大集合》第2节:学会编程就可以成为软件工程师吗
    C# ListView 控件使用
  • 原文地址:https://blog.csdn.net/q__y__L/article/details/126306548