C#设计模式:原型模式(Prototype)及深拷贝、浅拷贝

原型模式(Prototype)

定义:

原型模式:用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。被复制的实例被称为原型,这个原型是可定制的。

Prototype Pattern也是一种创建型模式,它关注的是大量相同或相似对象的创建问题。应用原型模式就是建立一个原型,然后通过对原型来进行复制的方法,来产生一个和原型相同或相似的新对象,或者说用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。

模式中的角色

抽象原型类(Abstract Prototype):提供一个克隆接口

具体原型类(Concrete Prototype): 实现了克隆接口的具体原型类 

原型模式的优点有:

    1. 原型模式向客户隐藏了创建新实例的复杂性和具体的产品类,减少了客户知道的名字的数目。
    2. 原型模式允通过注册原型实例就可以将一个具体产品类并入到系统中,客户可以在运行时刻建立和删除原型。
    3. 减少了子类的构造。原型模式是Clone一个原型而不是请求工厂方法创建一个,所以它不需要一个与具体产品类平行的Creator类层次。
    4. 产品类不需要事先确定产品的等级结构,因为原型模式适用于任何的等级结构。
    5. 原型模式具有给一个应用软件动态加载新功能的能力。由于Prototype的独立性较高,可以很容易动态加载新功能而不影响旧系统。

原型模式的缺点有:

原型模式的最重要缺点就是每一个类必须配备一个Clone方法,而且这个Clone方法需要对类的功能进行通盘考虑。这对全新的类来说不是很难,但对已有的类进行改造时,不一定是容易的事。 

原型模式可以适用于以下情形:

    1. 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等;
    2. 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式;
    3. 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用;
    4. 当一个系统应该独立于它的产品创建、构成和表示时;
    5. 当要实例化的类是在运行时刻指定时,例如通过动态装载来创建一个类;
    6. 为了避免创建一个与产品类层次平行的工厂类层次时;
    7. 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并Clone它们可能比每次用合适的状态手工实例化该类更方便一些。 

浅拷贝与深拷贝

浅度复制(Shallow Copy):将原来对象中的所有字段逐个复制到一个新对象,如果字段是值类型,则简单地复制一个副本到新对象,改变新对象的值类型字段不会影响原对象;如果字段是引用类型,则复制的是引用,改变目标对象中引用类型字段的值将会影响原对象

深度复制(Deep Copy):与浅复制不同之处在于对引用类型的处理,深复制将新对象中引用类型字段指向复制过的新对象,改变新对象中引用的任何对象,不会影响到原来的对象中对应字段的内容

值类型和引用类型的知识点可以了解:值类型和引用类型深入理解

浅拷贝与深拷贝示例:

拷贝对象School和Student:

    [Serializable]
    public class Student : ICloneable
    {
        private string name = "xxx";
        private int age = 0;

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public int Age
        {
            get { return age; }
            set { age = value; }
        }

        /// <summary>
        /// 新建对象实现克隆
        /// </summary>
        /// <returns></returns>
        public Student NewClone()
        {
            return new Student() { age = this.age, name = this.name };
        }

        /// <summary>
        /// 实现ICloneable接口
        /// </summary>
        /// <returns></returns>
        public object Clone()
        {
            return this.MemberwiseClone();
        }
    }

    [Serializable]
    public class School : ICloneable
    {
        private string m_Name = "init";
        private int m_Number = -1;
        private Student m_Student;

        public string Name
        {
            get { return m_Name; }
            set { m_Name = value; }
        }

        public int Number
        {
            get { return m_Number; }
            set { m_Number = value; }
        }

        public Student Student
        {
            get { return m_Student; }
            set { m_Student = value; }
        }

        /// <summary>
        /// 新建对象实现克隆,如果属性是引用类型,需要一层层new赋值,直到属性是值类型为止
        /// </summary>
        /// <returns></returns>
        public School NewClone()
        {
            return new School()
            {
                m_Name = this.m_Name,
                m_Number = this.Number,
                Student = this.Student.NewClone()
            };
        }

        /// <summary>
        /// 实现ICloneable接口
        /// </summary>
        /// <returns></returns>
        public object Clone()
        {
            return this.MemberwiseClone();
        }
    }

序列化拷贝和反射拷贝:

     public static class HelperTools
    {
        /// <summary>
        /// 序列化深拷贝
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static T SerializableClone<T>(T source)
        {
            if (!typeof(T).IsSerializable)
            {
                throw new ArgumentException("The type must be serializable.", source.GetType().ToString());
            }
            if (Object.ReferenceEquals(source, null))
            {
                return default(T);
            }
            IFormatter formatter = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream())
            {
                formatter.Serialize(ms, source);
                ms.Seek(0, SeekOrigin.Begin);
                return (T)formatter.Deserialize(ms);
            }
        }

        /// <summary>
        /// 反射属性浅拷贝
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static T PropertyClone<T>(T t)
        {
            if (Object.ReferenceEquals(t, null))
            {
                return default(T);
            }
            Type type = t.GetType();
            PropertyInfo[] propertyInfos = type.GetProperties();
            Object obj = Activator.CreateInstance<T>();
            Object p = type.InvokeMember("", BindingFlags.CreateInstance, null, t, null);
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                if (propertyInfo.CanWrite)
                {
                    object value = propertyInfo.GetValue(t, null);
                    propertyInfo.SetValue(obj, value, null);
                }
            }
            return (T)obj;
        }
    }
     public class Program
    {
        static void Main(string[] args)
        {
            School school = new School()
            {
                Name = "德源小学",
                Number = 0,
                Student = new Student()
                {
                    Name = "兔基斯",
                    Age = 18
                }
            };
            Console.WriteLine("************************原始值*****************************");
            ShowSchoolInfo(school);

            Console.WriteLine("************************序列化深拷贝*****************************");
            School serSchool = HelperTools.SerializableClone(school);
            serSchool.Name = "序列化";
            serSchool.Number = 1;
            serSchool.Student.Name = "xuliehua";
            serSchool.Student.Age = 20;
            ShowSchoolInfo(serSchool);
            ShowSchoolInfo(school);

            Console.WriteLine("************************新建对象深拷贝****************************");
            School newSchool = (School)school.NewClone();
            newSchool.Name = "new对象";
            newSchool.Number = 2;
            newSchool.Student.Name = "newObject";
            newSchool.Student.Age = 22;
            ShowSchoolInfo(newSchool);
            ShowSchoolInfo(school);


            Console.WriteLine("************************属性反射浅拷贝*****************************");
            School proSchool = HelperTools.PropertyClone(school);
            proSchool.Name = "反射";
            proSchool.Number = 3;
            proSchool.Student.Name = "fanshe";
            proSchool.Student.Age = 21;
            ShowSchoolInfo(proSchool);
            ShowSchoolInfo(school);

            Console.WriteLine("************************克隆接口浅拷贝*****************************");
            School cloneSchool = (School)school.Clone();
            cloneSchool.Name = "克隆";
            cloneSchool.Number = 4;
            cloneSchool.Student.Name = "kelong";
            cloneSchool.Student.Age = 23;
            ShowSchoolInfo(cloneSchool);
            ShowSchoolInfo(school);

            Console.ReadLine();
        }
        public static void ShowSchoolInfo(School school)
        {
            Console.WriteLine("学校名:{0}, 学校编号:{1}, 学生名字:{2}, 学生年龄:{3}", school.Name, school.Number, school.Student.Name, school.Student.Age);
        }
    }

注:实现克隆方法必须继承ICloneable接口,序列化的类必须标明特性[Serializable]

总结:

原型设计模式总算告一段落,从原型设计模式出发,深拷贝、浅拷贝,引用类型、值类型,拷贝方式,再回到起点原始设计模式,这一趟收获颇多。

备注:
作者:Shengming Zeng
博客:http://www.cnblogs.com/zengming/

本文是原创,欢迎大家转载;但转载时必须注明文章来源,且在文章开头明显处给明链接。
<欢迎有不同想法或见解的同学一起探讨,共同进步>

原文地址:https://www.cnblogs.com/zengming/p/6041128.html