原型模式

    原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。

    原型模式的三个角色

    Prototype(抽象原型类):声明克隆方法的接口,是所有具体原型类的公共父类,它可是抽象类也可以是接口,甚至可以是具体实现类。
    ConcretePrototype(具体原型类):它实现抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。
    Client(客户端):在客户类中,让一个原型对象克隆自身从而创建一个新的对象。

    使用场景

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

    原型模式的工作原理

    原型模式的工作原理很简单:将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝自己来实现创建过程。由于在软件系统中我们经常会遇到需要创建多个相同或者相似对象的情况,因此原型模式在真实开发中的使用频率还是非常高的。原型模式是一种“另类”的创建型模式,创建克隆对象的工厂就是原型类自身,工厂方法由克隆方法来实现。

    需要注意的是通过克隆方法所创建的对象是全新的对象,它们在内存中拥有新的地址,通常对克隆所产生的对象进行修改对原型对象不会造成任何影响,每一个克隆对象都是相互独立的。通过不同的方式修改可以得到一系列相似但不完全相同的对象。所以需要深复制

    UML图

    原型模式-设计模式(C#) - 图1

    简历的原型实现(浅复制)

    1. class Program
    2. {
    3. //客户端调用代码
    4. static void Main(string[] args)
    5. {
    6. Resume a = new Resume("大鸟");
    7. a.SetPersonalInfo("男","29");
    8. a.SetWorkExperience("1998-2000", "XX公司");
    9. Resume b = (Resume)a.Clone();
    10. b.SetWorkExperience("1998-2006", "YY企业");
    11. Resume c = (Resume)a.Clone();
    12. c.SetPersonalInfo("男", "24");
    13. a.Display();
    14. b.Display();
    15. c.Display();
    16. Console.Read();
    17. }
    18. }
    19. //简历类
    20. class Resume:ICloneable
    21. {
    22. private string name;
    23. private string sex;
    24. private string age;
    25. private string timeArea;
    26. private string company;
    27. public Resume(string name)
    28. {
    29. this.name = name;
    30. }
    31. //设置个人信息
    32. public void SetPersonalInfo(string sex,string age)
    33. {
    34. this.sex = sex;
    35. this.age = age;
    36. }
    37. //设置工作经历
    38. public void SetWorkExperience(string timeArea,string company)
    39. {
    40. this.timeArea = timeArea;
    41. this.company = company;
    42. }
    43. //显示
    44. public void Display()
    45. {
    46. Console.WriteLine("{0} {1} {2}", name, sex, age);
    47. Console.WriteLine("工作经历:{0} (1}", timeArea, company);
    48. }
    49. public object Clone()
    50. {
    51. return (object)this.MemberwiseClone();
    52. }
    53. }
    54. //结果显示
    55. //大鸟 男 29
    56. //工作经历 1998-2000 XX公司
    57. //大鸟 男 29
    58. //工作经历 1998-2006 YY公司
    59. //大鸟 男 24
    60. //工作经历 1998-2000 XX公司

    上面代码实现的“简历”对象里数据都是string型的,而string是一种拥有值类型特点的特殊引用类型,MemberwiseClone()方法是这样,如果字段是值类型的,则是对该字段执行逐位复制,如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。就是说你的“简历”类当中有对象引用,那么引用的对象数据是不会被克隆过来的。

    如果还是不太清楚,就得引入基本数据类型与引用数据类型的概念了,详情见深拷贝与浅拷贝的区别

    简历的原型实现(深复制)

    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. public object Clone()
    16. {
    17. return (Object)this.MemberwiseClone();
    18. }
    19. }
    20. //简历类
    21. class Resume:ICloneable
    22. {
    23. private string name;
    24. private string sex;
    25. private string age;
    26. private WorkExperience work;
    27. public Resume(string name)
    28. {
    29. this.name = name;
    30. work=new WorkExperience();
    31. }
    32. private Resume(WorkExperience work)
    33. {
    34. this.work = (WorkExperience)work.Clone();
    35. }
    36. //设置个人信息
    37. public void SetPersonalInfo(string sex,string age)
    38. {
    39. this.sex = sex;
    40. this.age = age;
    41. }
    42. //设置工作经历
    43. public void SetWorkExperience(string workDate,string company)
    44. {
    45. work.WorkDate = workDate;
    46. work.Company = company;
    47. }
    48. //显示
    49. public void Display()
    50. {
    51. Console.WriteLine("{0} {1} {2}", name, sex, age);
    52. Console.WriteLine("工作经历: {0} {1} ", work.WorkDate, work.Company);
    53. }
    54. public Object Clone()
    55. {
    56. Resume obj = new Resume(this.work);
    57. obj.name = this.name;
    58. obj.sex = this.sex;
    59. obj.age = this.age;
    60. return obj;
    61. }
    62. }