原型模式——创建型模式

时间秒杀一切 提交于 2019-12-09 09:50:56

思路:

马上又到找工作的时候了,当我们在准备一份份简历的时候有没有考虑过这样一个问题?

面对不同的工作岗位我们需要准备不同的求职简历,但是这样的几份不同的简历中还是有相当大的部分是相同的,我们如果每一份都从头开始重新制作,无疑是做了很多的无用功,浪费了很多时间。

那么,我们有没有办法不用重新new一个简历,只是对某一个原件进行适当的修改,就能实现这个功能呢?

别忘了,我们是程序员呀,程序员别的不一定行,Ctrl+C和Ctrl+V还是很溜的。复制下来对需要修改的部分进行修改不就行了?

但是同样别忘了,我们是程序员,如果这样一个问题用程序的思维来看,又会是什么样呢?

我们需要一个简历类,我们可以设置个人信息、工作经历,并且把它们显示出来。我们可以怎么写呢?

    class Program
    {
        static void Main(string[] args)
        {
            Resume a;
            a = new Resume("张三");
            a.SetPersonalInfo("男", "22");
            a.SetWorkExperience("1998-2000", "XX公司");
            a.SetAimCompany("Google");

            Resume b =  new Resume("张三");
            b = a;
            a.Display();
            b.Display();

            b.SetAimCompany("IBM");
            a.Display();
            b.Display();
            Console.Read();
        }
    }

    class Resume  //简历
    {
        private string name;
        private string sex;
        private string age;
        private string timeArea;
        private string company;
        private string AimCompany;

        public Resume(string name)
        {
            this.name = name;
        }

        //设置个人信息
        public void SetPersonalInfo(string sex, string age)
        {
            this.sex = sex;
            this.age = age;
        }

        //设置工作经历
        public void SetWorkExperience(string timeArea, string company)
        {
            this.timeArea = timeArea;
            this.company = company;
        }

        //设置个人信息
        public void SetAimCompany(string AimCompany)
        {
            this.AimCompany = AimCompany;
        }

        //显示
        public void Display()
        {
            Console.WriteLine("尊敬的" + AimCompany + "公司领导,您好:");
            Console.WriteLine("{0} {1} {2}", name, sex, age);
            Console.WriteLine("工作经历:{0} {1}", timeArea, company);
            Console.WriteLine("");
        }
    }

那么由以上代码的运行结果我们可以看到,当我们改变b的工作经历的时候,a的工作经历同样改变了。这是什么原因呢?因为

b=a;

这行代码执行的结果是,将a指向b的内容,并没有给a分配内存空间。所以改变了b实际上改变了a指向的内容。即并没有实现克隆的效果。

事实上,对于C#而言,因为克隆的常用,它在System命名空间里提供了ICloneable接口,其中一个惟一的方法就是Clone,我们只需要实现这个接口就好了,而不用去写它的原型抽象类了。

    class Program
    {
        static void Main(string[] args)
        {
            Resume a = new Resume("张三");
            a.SetPersonalInfo("男", "22");
            a.SetWorkExperience("1998-2000", "XX公司");
            a.SetAimCompany("Google");

            Resume b = (Resume)a.Clone() ;
            a.Display();
            b.Display();

            b.SetAimCompany("IBM");
            a.Display();
            b.Display();
            Console.Read();
        }
    }

    //简历
    class Resume :  ICloneable
    {
        private string name;
        private string sex;
        private string age;
        private string timeArea;
        private string company;
        private string AimCompany;

        public Resume(string name)
        {
            this.name = name;
        }

        //设置个人信息
        public void SetPersonalInfo(string sex, string age)
        {
            this.sex = sex;
            this.age = age;
        }

        //设置工作经历
        public void SetWorkExperience(string timeArea, string company)
        {
            this.timeArea = timeArea;
            this.company = company;
        }

        //设置个人信息
        public void SetAimCompany(string AimCompany)
        {
            this.AimCompany = AimCompany;
        }


        //显示
        public void Display()
        {
            Console.WriteLine("尊敬的" + AimCompany + "公司领导,您好:");
            Console.WriteLine("{0} {1} {2}", name, sex, age);
            Console.WriteLine("工作经历:{0} {1}", timeArea, company);
            Console.WriteLine("");
        }

        public Object Clone()
        {
            return this.MemberwiseClone();//浅复制
        }

    }

有运行结果我们可以看出,对b进行改变并不会改变a的结果,即实现了克隆的效果。

但需要注意的是,这里的克隆其实只是浅克隆。

值类型在克隆的时候是逐位复制,为深复制。而引用类型在复制时不复制引用的对象,只复制引用,为浅复制。而在C#中,string类型为特殊的引用类型,他可以被当做值类型进行深复制。所以在这里只是进行浅复制就可以达到效果。

但是一旦需要克隆类类型的变量的时候,不进行深复制是不行的。例如,我们的简历类中有设置身份证的方法,而在实际中我们一般会有一个身份证类,其间有身份证属性和设置身份证号的方法。这样就需要我们提供一个对这些引用类型实现深复制的方法。

    class Program
    {
        static void Main(string[] args)
        {
            Resume a = new Resume("张三");
            a.SetPersonalInfo("男", "22");
            a.SetWorkExperience("1998-2000", "XX公司");
            a.SetAimCompany("Google");

            Resume b = (Resume)a.DeepClone();
            a.Display();
            b.Display();

            b.SetAimCompany("IBM");
            b.idinfo.IdNumber = 56789;
            a.Display();
            b.Display();
            Console.Read();
        }
    }

    public class IDInfo
    {
        public int IdNumber;
        public IDInfo(int IdNumber)
        {
            this.IdNumber = IdNumber;
        }
    }

    //简历
    class Resume : ICloneable
    {
        private string name;
        private string sex;
        private string age;
        private string timeArea;
        private string company;
        private string AimCompany;
        public IDInfo idinfo;

        public Resume(string name)
        {
            this.name = name;
            idinfo = new IDInfo(123456);
        }

        //设置个人信息
        public void SetPersonalInfo(string sex, string age)
        {
            this.sex = sex;
            this.age = age;
        }

        //设置工作经历
        public void SetWorkExperience(string timeArea, string company)
        {
            this.timeArea = timeArea;
            this.company = company;
        }

        //设置个人信息
        public void SetAimCompany(string AimCompany)
        {
            this.AimCompany = AimCompany;
        }


        //显示
        public void Display()
        {
            Console.WriteLine("尊敬的" + AimCompany + "公司领导,您好:");
            Console.WriteLine("{0} {1} {2}", name, sex, age);
            Console.WriteLine("工作经历:{0} {1}", timeArea, company);
            Console.WriteLine("ID号码:"+idinfo.IdNumber.ToString());
            Console.WriteLine("");
        }

        public Object Clone()
        {
            return this.MemberwiseClone();
        }

        public Object DeepClone()
        {
            Resume rsm = (Resume)this.MemberwiseClone();
            rsm.idinfo = new IDInfo(this.idinfo.IdNumber);
            return rsm;

        }

    }

以上,就是原型模式的思路和方法。

UML图:

吐槽:

原型模式就是从一个对象在创建另外一个可定制的对象,而且不需知道任何创建的细节。

优点:

一般在初始化信息不发生变化的情况下,克隆是最好的办法,这既隐藏了对象创建的细节,又对性能是大大的提高。因为它不需要从新初始化对象,而是动态的获得对象运行时的状态。

原型模式允许动态增加或减少产品类。

原型模式具有给一个应用软件动态加载新功能的能力。

产品类不需要非得有任何事先确定的等级结构 。

缺点:

每一个类必须配备一个克隆方法。而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事。

在实现深克隆时需要编写较为复杂的代码。

使用情景:

创建新对象成本较大(CPU,初始化)。

系统要保存对象的状态,对象状态变化很小。

当一个类的实例只有几个不同状态组合时,建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化更为方便。

 

本菜鸟的疑难杂症:

1、在C#中类类型是引用类型,所以复制时默认浅复制。而string是一种特殊的引用类型,在处理的时候底层会把它当做值类型处理。

2、浅复制表明,被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来对象。要实现复制之后引用类型不指向同一个对象,就需要把复制的对象所引用的对象都复制一遍,即深复制。深复制把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!