zoukankan      html  css  js  c++  java
  • 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操作是比较困难得;而且原型模式在实现深层次复制时,需要编写一定量的代码。

    适用场景

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

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

  • 相关阅读:
    为调试JavaScript添加输出窗口
    后悔自己没有学好数学
    IEnumeralbe<T>被误用一例
    开发软件真是一件有意思的事情
    在网页上实现WinForm控件:ComboBox
    WinForm异步编程中一个容易忽视的问题
    网页上的DataGridView
    用Excel生成代码
    游戏处女作 打方块
    用GDI+保存Image到流时的一个有趣现象
  • 原文地址:https://www.cnblogs.com/sagahu/p/2711157.html
Copyright © 2011-2022 走看看