设计模式——原型模式(Prototype)

用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。——DP

UML类图

设计模式——原型模式(Prototype)_第1张图片

模式说明

如果把在一张纸上手写一篇简历的过程看成是类的实例化过程,那么通过原型模式创建对象的过程就是拿着这张纸到复印机上复印的过程。即原型模式中的一个对象可以自我克隆生成另一个对象,而无需再次通过类的初始化过程。由于克隆操作比较常见,.NET已经提供了ICloneable接口(包含唯一一个方法Clone),所以在.NET中,只要实现这个接口,就算实现原型模式了。

下面的示例中,定义了两个类,一个简历类和一个地址类,并且两个类都实现ICloneable接口。

    /// <summary>
    /// 在.Net中提供了ICloneable接口,无需再定义一个父类
    /// </summary>
    class Resume : ICloneable
    {
        public string Name { get; set; }

        public int Age { get; set; }

        public Address Addr { get; set; }

        public string Remark { get; set; }

        public Resume(string name)
        {
            Addr = new Address();
            this.Name = name;
        }

        // 原型模式
        public object Clone()
        {
            Resume resume = this.MemberwiseClone() as Resume;    //.NET中可以用MemberwiseClone方法创建自身的浅表副本
            resume.Addr = this.Addr.Clone() as Address;

            return resume;
        }

        public override string ToString()
        {
            return string.Format("姓名:{0},年龄:{1},地址:{2}——{3}", Name, Age, Addr.City + Addr.Street, Remark);
        }
    }

    class Address : ICloneable
    {
        public string City { get; set; }

        public string Street { get; set; }

        //原型模式
        public object Clone()
        {
            return this.MemberwiseClone();
        }
    }

对于原型模式的实现,要注意一点,就是复制的深度问题。由于MemberwiseClone方法,实现的是浅拷贝,即被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。这样如果Resume类的Clone方法直接返回this. MemberwiseClone(),那么新对象和原对象的Addr属性将引用相同的Address对象,这里对this. MemberwiseClone()返回的浅拷贝对象的Addr属性,再次进行一次克隆,以便对Resume对象的Clone操作后,能生成一个完全独立的对象。

这样在客户端调用时,就完全可以通过对象的克隆来创建更多的新对象:

        static void Main(string[] args)
        {
            /*
             * 原型模式 
             * 复制简历
             * 
             * 
             * 原型模式的实质是对于构造非常复杂的情况,比如在构造函数的时候 
             * 需要远程读取信息或者要完成许多复杂操作的时候 这时候用clone就会非常方便地获取其他实例的状态
             * 
             * 
             * 用了DataTable的Clone方法就知道了,为什么DataTable要这个方法,原因是当得到一个DataTable对象的时候
             * 又想创建一个一样的DataTable的话,如果没有Clone,那么就要创建Columns,这个是很麻烦的
             * 但是有了Clone就很爽了,直接调用就行
             *  
             */

            Resume resume1 = new Resume("我的简历模板");
            resume1.Age = 18;           //梦想着还是18岁……
            resume1.Addr.City = "湖州市";
            resume1.Addr.Street = "二环西路";
            resume1.Remark = "这份简历是自己留着,作为复印模板的。";

            Resume resume2 = (Resume)resume1.Clone();   //对象克隆
            resume2.Name = "苦哥";
            resume2.Age = 24;
            resume2.Remark = "这是给软件公司的简历";

            Resume resume3 = (Resume)resume2.Clone();   //对象克隆
            resume3.Name = "苦哥大人";
            resume3.Addr.City = "合肥市";
            resume3.Addr.Street = "长江路";
            resume3.Remark = "这是投到合肥的简历,写成住合肥,是不是会亲近点?";


            Console.WriteLine(resume1.ToString());
            Console.WriteLine(resume2.ToString());
            Console.WriteLine(resume3.ToString());

            Console.Read();
        }

总结

原型模式的实现过程中,要知道两个概念:

  • 浅复制:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。
  • 深复制:把引用对象的变量指向复制过来的新对象,而不是原有的被引用的对象。

还有一点要考虑到的就是深复制的深度问题,如果对象引用的对象内,又包含了对其他对象的引用……这样在深复制时要考虑复制多少层的问题,对于这种情况,可以通过序列化与反序列化的方式,来达到对象深复制的目的。

参考

  1. 程杰老师  《大话设计模式》

你可能感兴趣的:(prototype)