01.04 原型模式

设计模式——原型模式

需求

比如有个对象,在某一时刻该对象中已经包含了一些有效值,此时可能会需要一个和该对象完全相同的新对象,并且此后对对象的任何修改都不会影响到原来对象中的值。也就是说,新对象与原来的对象是两个完全独立的对象,仅仅新对象的初始值是由原来对象确定的。这样的要求,显然可以以原来的对象为原型,复制出一个新对象来(好像没什么问题,其实在OOP中要注意浅表复制与深度克隆的区别),这种思想就是原型模式(Prototype Pattern)。

定义

意图:用原型对象指定要创建的对象的类型,并且通过拷贝这些原型对象创建新的同类型对象。

原理:通过拷贝一个现有的对象来生成新的同类型的对象。

原型设计模式包括两部分:(1)原型类(Abstract Prototype),描述类的原型,并且定义一个抽象的拷贝自身的方法;(2)实体的子类型(Concrete Prototype),实现对自身的对象拷贝。

原型模式同工厂模式,同样对客户隐藏了对象的创建工作。但是,与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。

拷贝对象必须注意“浅表复制”和“深度克隆”的区别。当拷贝对象时,首先根据旧对象,创建一个同类型新对象,然后把旧对象的非静态字段复制到新对象。对于值类型字段复制就是简单复制字段值就可以了;而引用类型的复制就要分为两种情况:(1)复制引用,(2)复制引用的对象。在对象拷贝时,对于引用型字段,仅仅简单地复制其引用就是所谓的“浅表复制”,而完全地复制引用的对象,则成为“深度拷贝”。

在.NET中,祖先类System.Object为对象克隆提供了一个受保护方法MemberwiseClone(),可以创建当前对象的浅表副本。至于深度克隆代码,只有你自己来编写了。

案例

    using System.IO;

    using System.Runtime.Serialization.Formatters.Binary;

    class Program

    {

        static voidMain(string[] args)

        {

            // 客户程序

            // 第一条狗:奥巴马、52,4条腿

            Dog2 dog1 = new Dog2();

            dog1.Name = "奥巴马"; dog1.Age = 52;

            dog1.Leg = new Leg(); dog1.Leg.LegCount = 4;

            // 第二条狗:小泉纯一郎、60、3条腿

            Dog2 dog2 = dog1.Clone() as Dog2;

            Console.WriteLine("刚刚克隆出第2条狗,它的数据是如下:");

            Console.WriteLine(string.Format("\t{0},{1},{2}", dog2.Name, dog2.Age, dog2.Leg.LegCount));

            dog2.Name = "小泉纯一郎"; dog2.Age = 60;

            dog2.Leg.LegCount = 3;

            Console.WriteLine("修改后它的数据是如下:");

            Console.WriteLine(string.Format("\t{0},{1},{2}", dog2.Name, dog2.Age, dog2.Leg.LegCount));

            Console.WriteLine("第1条狗的数据是如下,没有因为第2条狗的修改而变化:");

            Console.WriteLine(string.Format("\t{0},{1},{2}", dog1.Name, dog1.Age, dog1.Leg.LegCount));

        }

    }

    // 原型类

    public interface IDogPrototype

    {

        IDogPrototype Clone();

    }

    [Serializable]

    public class Leg 

    {

        public int LegCount;

    }

    // 这个子类代码演示第一种深度克隆的实现方法

    public class Dog : IDogPrototype

    {

        public string Name; // string的操作与值类型大多数时候相同,但是它是特殊的引用型。

        public int Age;

        public Leg Leg; // 引用型类型的复制需要深度克隆

        public IDogPrototype Clone()

        {

            Dog newDog = this.MemberwiseClone() as Dog; // 本行代码浅表复制,下面再进行深度克隆

            // 简单地实现深度克隆,需要了解类成员,还需要大量的编码

            newDog.Leg = new Leg();

            newDog.Leg.LegCount = this.Leg.LegCount;

            return newDog;

        }

    }

   

    // 这个子类通过序列化技术实现深度克隆

    [Serializable]

    public class Dog2 : IDogPrototype

    {

        public string Name;

        public int Age;

        public Leg Leg;

        public IDogPrototype Clone()

        {

            IDogPrototype newDog = null;

            MemoryStream memoryStream = new MemoryStream();

            BinaryFormatter binaryFormatter = new BinaryFormatter();

            binaryFormatter.Serialize(memoryStream, this);

            memoryStream.Position = 0;

            newDog = (IDogPrototype)binaryFormatter.Deserialize(memoryStream);

            memoryStream.Close();

            return newDog;

        }

    }

优缺点

优点:在原型模式中,对象的克隆在类自身内部实现,所以可以动态地增加产品类,而且对整体结构没有影响。

缺点:由于原型模式需要给每个类都配备一个克隆方法,这就需要在设计类时通盘考虑,因为在已有的类上添加clone操作是比较困难得;而且原型模式在实现深层次复制时,需要编写一定量的代码。

适用场景

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

当系统需要创建的对象是动态加载的,而且产品具有一定层次时,需要使用原型模式。原型模式的具体的创建过程,是由对象本身提供,这样我们在很多的场景下,我们可以很方便的快速的构建的对象,比通过其他几类的创建型模式,效果要好的多,而且代价也小很多。打个比方,原型模式对于系统的扩展,可以做到无缝的扩展,为什么这么说呢?比如其他的创建型工厂,如果新增一个对象类型,那么我们不管是修改配置文件的方式,还是修改代码的形式,无疑我们都是需要进行修改的,对于我们大家通用的公共应用来说这无疑是危险的,那么通过原型模式,则可以解决这样的问题,因为类型本身实现这样的方法即可,但是也要注意它的缺点,每个对象都实现这样的方法,无疑是很大的工作量,但是在某些特殊的环境下,或者实际的项目中,可能原型模式是好的选择。

原文地址:https://www.cnblogs.com/sagahu/p/2711157.html