小看--原型模式

Posted 山的那边是什么

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小看--原型模式相关的知识,希望对你有一定的参考价值。

    前面我们说过了单例模式,是用来强制保证同一个进程内只有一个对象;享元模式:利用第三方工厂来创建对象,也可以保证一个进程内只有一个对象(非强制保证);那么今天我们来讲讲原型模式;先不说概念了,直接看下面一个例子;

   (一) 原型模式

     下面是一个StudentSingleton,里面是可以保证一个进程内只有一个对象了的(简单的保证)

 public class StudentSingleton {
        private StudentSingleton()
        {
            Thread.Sleep(2000);
            long lResult = 0;
            for (var i = 0; i <= 100000; i++)
            {
                lResult += i;
            }
            Console.WriteLine($"{this.GetType().Name}被构造");
        }

        /// <summary>
        /// 单例
        /// </summary>
        private static  StudentSingleton _studentSingleton=new StudentSingleton();

        public static StudentSingleton CreateInstance()
        {
            return _studentSingleton;
        }
    }

     下面我们在两个地方要用到这个StudentSingleton的对象;

 var studentSingleTone1 = StudentSingleton.CreateInstance();
            studentSingleTone1.Name = "zhangzhen";
            studentSingleTone1.Id = "111";
            Console.WriteLine("第一个对象的Name"+studentSingleTone1.Name);
            Console.WriteLine("第一个对象的Id" + studentSingleTone1.Id);

            var studentSingleTone2 = StudentSingleton.CreateInstance();
            studentSingleTone2.Name = "1zhangzhen1";
            studentSingleTone2.Id = "222";
            Console.WriteLine("第二个对象的Name" + studentSingleTone2.Name);
            Console.WriteLine("第二个对象的Id" + studentSingleTone2.Id);
            Console.WriteLine("----------------");
            Console.WriteLine("第一个对象的Name" + studentSingleTone1.Name);
            Console.WriteLine("第一个对象的Id" + studentSingleTone1.Id);

    

第二个对象的值改变和会影响第一个(这个也是单例模式的缺点),于是我们就提出,有没有这样一种方法,能够保证大家一起修改,不会互相影响呢?----------接下来解决这个问题就是原型模式,原型模式是为了解决同一个进程如何保证只被构造一次,然后还可以做到互不影响(对象重用);

    (二)浅拷贝

        那接下来就来解决我们刚刚说的那个问题,上面的单例模式,虽然做到了同一个进程只有一个对象,但是对象操作相互影响。

        原型模式:1、保证对象同一个进程内,只被构造一次。2、还要做到对象操作相互不影响。

        解决思路就是在单例模式的基础,既然要做到对象操作相互不影响,那么就每次创建对象返回的时候,返回一份拷贝。

 public class StudentPrototype {

        public string Id { get; set; }
        public string Name { get; set; }

        private StudentPrototype() {
            Thread.Sleep(2000);
            long lResult = 0;
            for (var i = 0; i <= 100000; i++)
            {
                lResult += i;
            }
            Console.WriteLine($"{this.GetType().Name}被构造");
        }

        /// <summary>
        /// 单例
        /// </summary>
        private static StudentPrototype _studentPrototype =new StudentPrototype();

        public static StudentPrototype CreateInstance()
        {
            StudentPrototype sutStudentPrototype =(StudentPrototype) _studentPrototype.MemberwiseClone();
            return sutStudentPrototype;

        }
    }

  

      上面这个例子,我们做了一份内存拷贝之后,就成功的实现了对象只被构造一次,操作之间互不影响。

    (三) 深拷贝

       继续刚刚的例子,我们给StudentPrototype加上一属性叫做班级(Class);

       

 public class StudentPrototype {

        public string Id { get; set; }
        public string Name { get; set; }

        public Class Class { get; set; }

        private StudentPrototype() {
            Thread.Sleep(2000);
            long lResult = 0;
            for (var i = 0; i <= 100000; i++)
            {
                lResult += i;
            }
            Console.WriteLine($"{this.GetType().Name}被构造");
        }

        /// <summary>
        /// 单例
        /// </summary>
        private static StudentPrototype _studentPrototype =new StudentPrototype()
        {
            Id = "111",
            Name = "Fool",
            Class = new Class() {
                ClassId = 3,
                ClassName = "测试"
            }
        };

        public static StudentPrototype CreateInstance()
        {
            StudentPrototype sutStudentPrototype =(StudentPrototype) _studentPrototype.MemberwiseClone();
            return sutStudentPrototype;

        }
    }


    public class Class
    {
        public int ClassId { get; set; }

        public string ClassName { get; set; }
    }

       

     又发现对于引用类型呢,我们之前做法是失效的,这是为啥呢,因为我们上面的MemberwiseClone()方法是可以实现浅拷贝,就是只是复制了对象的引用(没有复制到具体的值)。大概过程如下

     

 

       这个就是浅拷贝,浅拷贝拷贝的是对象的引用(没有把整个对象一起拷贝过来);浅拷贝只是复制对象本身(就是该对象所在堆中的一块连续地址内容)

       深拷贝就是把整个对象都一起拷贝过来。

       对于C#来说,值类型的复制就是全盘复制(string),引用类型的复制,浅拷贝是只复制引用。

       对我们来说,如何实现对引用类型的全盘复制才是最主要的。如何实现深拷贝。

       ---接着我们的案例,我们暂时不说如何深拷贝先,我们先讲一些其他做法先。

      竟然是因为引用类型只是复制引用,那我们每次拷贝的是去改变它的引用不就可以做到了互不影响吗,基于这个思路,我们代码做如下改变;

     

    这样子也能够实现了我们的对引用类型的拷贝。

         但是就像很多人说的那样,这种做法不太好,不太合理。因为你类里面嵌套多个类,多个类里面又嵌套很多的时候,这样做就会非常尴尬。所以下面来介绍深拷贝;

        就是利用序列化和反序列化来实现---注意:因为我们要进行序列化,需要给对应的类加上 [Serializable];

/// <summary>
    /// 序列化和反序列化实现
    /// </summary>
    public class SerializeHelper {
        public static T DeepClone<T>(T t)
        {
            T tObj;
            //把对象序列化到内存。
            using (MemoryStream memoryStream = new MemoryStream()){
                BinaryFormatter bf=new BinaryFormatter();
                bf.Serialize(memoryStream,t);
                memoryStream.Seek(0, SeekOrigin.Begin);
                tObj=(T)bf.Deserialize(memoryStream);
                memoryStream.Close();
                
            }
            return tObj;
        }
    }

 

        

   (四)为啥string是引用类型,浅拷贝的时候,会拷贝到值;

       简单的讲就是,字符串的不可变性,每次给字符串进行赋值是,其实是发生了这些操作,先看看内存里面是否存在相同的字符串,如果不存在就去开辟一个新的地址,那所以说,这就是为毛字符串复制的时候可以复制到值,因为字符串的值如果不同的话,就会去新开辟一个内存。

       总结:原型模式也是一种对象复用技术,希望通过上面能够对加深大家对原型模式的理解。

 

    

以上是关于小看--原型模式的主要内容,如果未能解决你的问题,请参考以下文章

架构师内功心法,只是单纯听说过的原型模式详解

小看--桥接模式

小看--职责链模式

小看--享元模式

小看--单例设计模式

小看--简单工厂