C#设计模式03-原型模式

来源:互联网 发布:网络听课神器 编辑:程序博客网 时间:2024/04/27 22:30

        好久没有写博客了,因为过年实在太忙了,家里也没有网络,今天第一天上班就先写一篇吧。话不多说,今天要写的内容是:原型模式,它也是创建型模式中的一种

        首先来看定义:使用原型实例指定待创建对象的类型,并且通过复制这个原型来 创建新的对象

        理解定义之前先看一幅图:


        孙悟空可以变出很多个和自己一模一样的猴子,这里的孙悟空就是原型类,原型类里面有一个Clone()方法可以复制他自己。有了这个方法他就可以自我复制出很多小猴子。原型模式和孙悟空自我复制自己这个模式差不多。

        接下来我们就来看看原型模式的中的三种角色:

        (1)Prototype(抽象原型类):这个类声明克隆(复制)方法(也就是Clone()方法),是所有具体原型类的父类,它可以是抽象类,接口或者具体的实现类

        (2)ConcretePrototype(具体原型类):这个类实现(继承)抽象原型类,实现(重写)Clone方法以返回自己的一个克隆对象

        (3)Client(客户端类):这个类通过调用Clone方法来克隆对象,返回值为Prototype

        在介绍原型模式之前,有两个概念需要解释一下,也就是深克隆(深复制)和浅克隆(浅复制)。我们知道,C#中,值类型的数据存放在内存栈空间中,而引用类型存放在内存堆空间中,并且在栈空间上保存了该对象的引用地址。执行浅克隆操作时,会复制原型对象本身和原型对象中包含的值类型成员,而引用类型并没有被复制,仅仅复制了引用类型的地址。对克隆对象中引用类型的操作将直接影响原型对象。 而执行深克隆操作时,不仅复制原型对象本身和原型对象中包含的值类型成员,而且会复制引用类型,所以对克隆对象中引用类型的操作不会影响到原型类型。

        C#中Object类型的MemberwiseClone方法可以帮助我们实现浅复制,而要想实现深复制的话,可以通过实现ICloneable接口来手动创建当前对象的副本。

        接下来我们先实现浅克隆,因为它比较简单。

        假设现在有一个客户管理系统,有一个客户类Customer,类里面有name字段表示客户名称,age字段表示客户年龄,还有一个Adress类型的成员变量表示客户的地址。

        现在通过原型模式来浅复制客户类。

       首先是我们的客户地址Adress类

namespace 原型模式{    public class Adress    {        public string province;//客户所在省份        public string county;//客户所在县名称        //..其他的村,街道等省略        public string postCode;//邮编            }}

        然后是我们的客户类Customer
namespace 原型模式{    public class Customer//客户类(具体原型类)    {        public string name;//客户姓名        public string Name        {            get { return name; }            set { name = value; }        }        private int age;        public int Age        {            get { return age; }            set { age = value; }        }        public Adress adress;        //使用MemberwiseClone方法实现浅克隆        public Customer Clone()        {            return this.MemberwiseClone() as Customer;        }    }}

         最后是我们的客户端类

namespace 原型模式{    class Program    {        static void Main(string[] args)        {            Customer c = new Customer();            Adress adress = new Adress() { province="山西",county="河津", postCode="043300"};            c.Name = "曹瑞鹏";            c.adress = adress;            Customer copy = c.Clone();            Console.WriteLine("客户对象是否相同:{0}",c==copy);            Console.WriteLine("地址是否相同:{0}", c.adress == copy.adress);            Console.WriteLine("客户年龄是否相同:{0}", ReferenceEquals(c.Age, copy.Age));            Console.WriteLine("客户姓名是否相同:{0}", ReferenceEquals(c.Name, copy.Name));            Console.ReadLine();        }    }}

       运行结果如下:


        从运行结果上可以清楚的看到,客户对象本身和客户年龄这两个变量都不相同了(也就是他们在内存中的地址不一致了)。而作为引用类型的地址和姓名这两个变量在内存中的地址是相同的,所以引用类型并没有被复制。这个就是浅复制。

       Object的ReferenceEquals方法(静态方法)用于测试两个引用是否指向同一个类的实例,也就是是否指向同一个地址。

       然后我们来实现深克隆,深克隆就是可以通过实现ICloneable接口来手动实现克隆

       地址类Adress和上面的一样,客户端类和客户类需要修改一下

namespace 原型模式{    public class Customer:ICloneable//客户类(具体原型类)    {        public string name;//客户姓名        public string Name        {            get { return name; }            set { name = value; }        }        private int age;        public int Age        {            get { return age; }            set { age = value; }        }        public Adress adress;        public  object Clone()//深克隆方法        {            Adress adress = new Adress() { province = "北京", county = "海淀", postCode = "002335" };            Customer copy = this.MemberwiseClone() as Customer;            copy.adress = adress;            copy.Name = "曹瑞鹏";            return copy;        }    }}
namespace 原型模式{    class Program    {        static void Main(string[] args)        {            Customer c = new Customer();            Customer copy = c.Clone() as Customer;            Console.WriteLine("客户对象是否相同:{0}",c==copy);            Console.WriteLine("地址是否相同:{0}", c.adress == copy.adress);            Console.WriteLine("客户年龄是否相同:{0}", ReferenceEquals(c.Age, copy.Age));            Console.WriteLine("客户姓名是否相同:{0}", ReferenceEquals(c.Name, copy.Name));            Console.ReadLine();        }    }}

运行结果如下:


        从运行结果可以看得出来,客户姓名和客户地址都成了false,而在浅复制中他们的比较结果是true。这就说明深复制操作会复制引用类型。

        除了可以通过实现ICloneable接口达到深复制之外,还可以通过反射,序列化等方式实现深复制,有兴趣的朋友可以自己去查阅相关文档资料或者书籍。 

        到这里原型模式就给大家介绍完了,最后简单介绍一下原型模式的优缺点:

        优点:复制对象的代价远小于创建对象的代价,所以当创建的对象较为复杂时,原型模式可以简化对象的创建过程

        缺点:每个类里面必学有一个Clone克隆方法,当对现有的类进行改造时,需要修改Clone方法。这违背了开闭原则,扩展性也不是很好。同时深克隆的实现会比较麻烦,因为当当前对象之间存在多重嵌套的时候,每一层对象都必须支持深克隆。

        原型类型的适用环境主要如下:

        创建对象的成本较高,并且需要保存对象的状态信息,而这些状态信息的变化比较小。

下一篇文章将给大家介绍单例模式


1 0
原创粉丝点击