zoukankan      html  css  js  c++  java
  • C#设计模式:原型模式(Prototype Pattern)

    一,原型模式:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。(包含深度克隆和浅克隆)

    主要面对的问题是:“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是他们却拥有比较稳定一致的接口。

    先上代码,

    PeoplePrototype.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace PrototypePattern
    {
        /// <summary>
        /// 如果序列化报错就加上Serializable这个特性
        /// </summary>
        [Serializable]
        public class PeoplePrototype
        {
            private static PeoplePrototype _peoplePrototype = null;
            private PeoplePrototype()
            {
                Console.WriteLine("{0}被创建了,线程ID{1}", this.GetType(), Thread.CurrentThread.ManagedThreadId);
            }
    
            static PeoplePrototype()
            {
                _peoplePrototype = new PeoplePrototype()
                {
                    Name = "小明",
                    Id = 1,
                    Dept = new Dept()
                    {
                        Id = 11,
                        Name = "技术部"
                    }
                };
            }
            /// <summary>
            /// 克隆一个对象
            /// </summary>
            /// <returns></returns>
            public static PeoplePrototype CreateInstance()
            {
                PeoplePrototype peoplePrototype = (PeoplePrototype)_peoplePrototype.MemberwiseClone();;
                return peoplePrototype;
            }
            /// <summary>
            /// 克隆一个对象
            /// </summary>
            /// <returns></returns>
            public static PeoplePrototype CreateInstanceDeep()
            {
                PeoplePrototype peoplePrototype = (PeoplePrototype)_peoplePrototype.MemberwiseClone();
                //深度克隆,重新实例化一个对象和开辟一个内存,所以克隆的结果指向的地址每次都不一样
                //可是这样操作麻烦,我们优化一下
                peoplePrototype.Dept = new Dept()
                {
                    Id = 11,
                    Name = "技术部"
                };
                return peoplePrototype;
            }
    
            public static PeoplePrototype CreateInstanceSerializa()
            {
                //通过序列化生成实体
                return SerializaUtil.DeepClone<PeoplePrototype>(_peoplePrototype);
            }
    
            public int Id { get; set; }     //值类型保存的是地址
            /// <summary>
            /// 为什么string也是引用类型不被覆盖?String字符串的值是一个定存的,重新改值会实例化一个新的对象,所以在遍历中不建议使用string,因为损耗性能
            /// </summary>
            public string Name { get; set; }  //为什么string也是引用类型不被覆盖?
            public Dept Dept { get; set; }   //引用类型保存的是地址,所以克隆出来的值是一样的
    
        }
    
        /// <summary>
        /// 如果序列化报错就加上Serializable这个特性
        /// </summary>
        [Serializable]
        public class Dept
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }
    }

    SerializaUtil.cs

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace PrototypePattern
    {
        /// <summary>
        /// 帮助类,创建对象
        /// </summary>
        public class SerializaUtil
        {
            /// <summary>
            /// 根据对象序列化
            /// </summary>
            /// <param name="target"></param>
            /// <returns></returns>
            public static string Serializa(object target)
            {
                using (MemoryStream stream = new MemoryStream())
                {
                    new BinaryFormatter().Serialize(stream, target);
                    return Convert.ToBase64String(stream.ToArray());
                }
            }
            /// <summary>
            /// 根据对象类型反序列化,生成新的实体对象
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="target"></param>
            /// <returns></returns>
            public static T Derializaable<T>(string target)
            {
                byte[] targetArray = Convert.FromBase64String(target);
    
                using (MemoryStream stream = new MemoryStream(targetArray))
                {
                    return (T)new BinaryFormatter().Deserialize(stream);
                }
            }
    
            /// <summary>
            /// 实体对象
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="t"></param>
            /// <returns></returns>
            public static T DeepClone<T>(T t)
            {
                return Derializaable<T>(Serializa(t));
            }
        }
    }

    Program.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace PrototypePattern
    {
        class Program
        {
            static void Main(string[] args)
            {
    
                Console.WriteLine("---------------------------------------");
                PeoplePrototype peoplePrototype1 = PeoplePrototype.CreateInstance();
                PeoplePrototype peoplePrototype2 = PeoplePrototype.CreateInstance();
                Console.WriteLine("{0},{1}", peoplePrototype1.Name, peoplePrototype1.Id);
                Console.WriteLine("{0},{1}", peoplePrototype2.Name, peoplePrototype2.Id);
    
                Console.WriteLine("---------------------------------------");
                PeoplePrototype peoplePrototype3 = PeoplePrototype.CreateInstance();
                PeoplePrototype peoplePrototype4 = PeoplePrototype.CreateInstance();
                peoplePrototype3.Name = "测试";  //修改peoplePrototype3,不影响peoplePrototype4的值,证明他们不是同一个引用对象
                Console.WriteLine("{0},{1}", peoplePrototype3.Name, peoplePrototype3.Id);
                Console.WriteLine("{0},{1}", peoplePrototype4.Name, peoplePrototype4.Id);
    
    
                Console.WriteLine("---------------------------------------");
                Console.WriteLine("浅克隆");
                PeoplePrototype peoplePrototype5 = PeoplePrototype.CreateInstance();
                PeoplePrototype peoplePrototype6 = PeoplePrototype.CreateInstance();
                peoplePrototype5.Dept.Name = "测试";  //修改peoplePrototype5的Dept.Name,影响peoplePrototype6的值,证明Dept同一个引用对象
                peoplePrototype5.Dept.Id = 55;  //修改peoplePrototype5的Dept.Id ,影响peoplePrototype6的值,证明Dept同一个引用对象
                Console.WriteLine("{0},{1}", peoplePrototype5.Dept.Name, peoplePrototype5.Dept.Id);
                Console.WriteLine("{0},{1}", peoplePrototype6.Dept.Name, peoplePrototype6.Dept.Id);
    
                Console.WriteLine("---------------------------------------");
                Console.WriteLine("深克隆");
                PeoplePrototype peoplePrototype7 = PeoplePrototype.CreateInstanceDeep();
                PeoplePrototype peoplePrototype8 = PeoplePrototype.CreateInstanceDeep();
                peoplePrototype7.Dept.Name = "测试";  //修改peoplePrototype5的Dept.Name,影响peoplePrototype6的值,证明Dept同一个引用对象
                peoplePrototype7.Dept.Id = 55;  //修改peoplePrototype5的Dept.Id ,影响peoplePrototype6的值,证明Dept同一个引用对象
                Console.WriteLine("{0},{1}", peoplePrototype7.Dept.Name, peoplePrototype7.Dept.Id);
                Console.WriteLine("{0},{1}", peoplePrototype8.Dept.Name, peoplePrototype8.Dept.Id);
    
                Console.WriteLine("---------------------------------------");
                Console.WriteLine("深克隆");
                PeoplePrototype peoplePrototype9 = PeoplePrototype.CreateInstanceSerializa();
                PeoplePrototype peoplePrototype10 = PeoplePrototype.CreateInstanceSerializa();
                peoplePrototype9.Dept.Name = "测试";  //修改peoplePrototype5的Dept.Name,影响peoplePrototype6的值,证明Dept同一个引用对象
                peoplePrototype9.Dept.Id = 55;  //修改peoplePrototype5的Dept.Id ,影响peoplePrototype6的值,证明Dept同一个引用对象
                Console.WriteLine("{0},{1}", peoplePrototype9.Dept.Name, peoplePrototype9.Dept.Id);
                Console.WriteLine("{0},{1}", peoplePrototype10.Dept.Name, peoplePrototype10.Dept.Id);
    
                Console.WriteLine();
            }
        }
    }

    执行结果如下

    二,总结下

    1,什么是浅克隆和深克隆,看代码运行结果解释

    1》浅克隆没有克隆我们对象中的引用类型,如我们的实体PeoplePrototype中的Dept,如下图

     

    可是这个时候我们就会疑惑了,string也是引用类型,为什么也会被克隆呢?

    2》我们理解下string的运行机制,string是特殊的引用类型,字符串的值是一个定存的,重新改值会实例化一个新的对象,所以在遍历中不建议使用string,因为损耗性能

    string ss = "33";
    ss = "44"; //此时 ss = "33";和ss = "44"; 的存储地址不一样的

    3》那Dept引用类型在克隆中的是怎样的逻辑呢?

    Dept引用类型在克隆中是他们没有初始化,但是都是指向一个内存空间,所以我们将浅克隆的dept属性更改是会同时更改其他克隆出来的对象的dept属性,如下图

     4》浅克隆的值类型不受影响

    5》那什么是深克隆呢?如下图结果

    深克隆的引用类型互不影响,实现的方法有两种

    1,在复制中操作引用类型初始化

            /// <summary>
            /// 深克隆一个对象
            /// </summary>
            /// <returns></returns>
            public static PeoplePrototype CreateInstanceDeep()
            {
                PeoplePrototype peoplePrototype = (PeoplePrototype)_peoplePrototype.MemberwiseClone();
                //深度克隆,重新实例化一个对象和开辟一个内存,所以克隆的结果指向的地址每次都不一样
                //可是这样操作麻烦,我们优化一下
                peoplePrototype.Dept = new Dept()
                {
                    Id = 11,
                    Name = "技术部"
                };
                return peoplePrototype;
            }

    2,使用序列化和反序列化创建对象(帮助类SerializaUtil)

            /// <summary>
            /// 深克隆一个对象
            /// </summary>
            /// <returns></returns>
            public static PeoplePrototype CreateInstanceSerializa()
            {
                //通过序列化生成实体
                return SerializaUtil.DeepClone<PeoplePrototype>(_peoplePrototype);
            }

    三,原型模式:“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是他们却拥有比较稳定一致的接口。

    1》复杂对象的指的是当创建该对象消耗资源过多

    2》面临的剧烈变化,比如发邮件,我们需要发N条,但是这N条邮件的对象每个人发送的信息也不同,所以导致实力出的的对象的也不完全一样

    3》稳定的接口值得是都是通过同一个方法将该对象发送出去,既是调用方法一般不存在变化,而是对象改变

    四,在什么情况下该选择原型模式?

    1》是类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等,如上结果所示,我们在初始化时消耗过多资源,这是就体现出原型模式的优势

    2》是通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式

    五,原型模式的浅度克隆和深度克隆是什么意思?

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

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

  • 相关阅读:
    链式表的操作
    顺序表的操作
    MDX基础
    1071 小赌怡情 (15 分)
    1069 微博转发抽奖 (20 分)
    1068 万绿丛中一点红 (20 分)
    1066 图像过滤 (15 分)
    02-线性结构2 一元多项式的乘法与加法运算 (20 分
    03-树1 树的同构 (25 分)
    03-树3 Tree Traversals Again (25 分)
  • 原文地址:https://www.cnblogs.com/May-day/p/8462507.html
Copyright © 2011-2022 走看看