设计模式之原型模式

        当我们在人才市场求职中,往往会向很多公司投递简历,并且简历一般会留在招聘员的手中。如果只准备了两三份的简历,那么求职机会是比较少的,若是准备很多份,会耗费大量时间去复印(手写的话更耗时间)。代码同样也是这种情况,如果某个类的构造函数执行特别耗时,则我们频繁实例化时会耗费大量的时间,那么有没有一种方法可以直接将已经实例化好的类复制一份出来,这样就避免因构造函数执行耗时导致的耗时耗性能的问题?

         有需求就会有实现,原型模式应运而生。其特点就是所需复制的类自身提供可复制自身的方法,同时将该方法封装在内部,外界使用时无需关心其实现方式,只调用提供的复制类的功能的接口即可获取一个复制出来的类。

        Talk is cheap. Show me the code.

/// <summary>
    /// 工作经历类,存放公司名称及工作年限
    /// </summary>
    class WorkExperence
    {
        public string CompanyName{ get; set; }
        public string Time { get; set; }

    }

    /// <summary>
    /// 简历类,同时实现 .Net提供的ICloneabel接口,用来克隆并返回自身
    /// </summary>
    class Resume : ICloneable
    {
        private string name;
        private string sex;
        private string age;
        WorkExperence work;

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

        public void SetPersonalInfo(string age,string sex)
        {
            this.age = age;
            this.sex = sex;
        }

        public void SetWorkExperence(string companyName,string time)
        {
            work.CompanyName = companyName;
            work.Time = time;
        }

        public void Display()
        {
            Console.WriteLine("{0} {1} {2} {3} {4}", name, age, sex, work.CompanyName, work.Time);
        }

        /// <summary>
        /// 实现克隆接口,返回自身
        /// </summary>
        /// <returns></returns>
        public object Clone()
        {
            return this.MemberwiseClone();
        }
    }

        上面的代码很简单,相信各位应该不难理解。接下来我们就来看看该如何使用。、

class Program
    {
        static void Main(string[] args)
        {
            Resume resume1 = new Resume("张三");
            resume1.SetPersonalInfo("22", "男");
            resume1.SetWorkExperence("XXX", "2001 - 2005");

            Resume resume2 = (Resume)resume1.Clone();
            resume2.SetWorkExperence("YYY", "2005 - 2008");

            Resume resume3 = (Resume)resume1.Clone();
            resume3.SetWorkExperence("ZZZ", "2008 - 2012");

            resume1.Display();
            resume2.Display();
            resume3.Display();

            Console.ReadKey();
        }
    }

        其使用方式如上述代码块所示,按照我们的写法应该是按顺序显示出XXX、YYY、ZZZ及其对应的工作年限,可事情真的这么简单吗?可以看看运行结果:

哎奇怪,为什么都只显示最后一次设置的工作经历的数据呢?这就涉及到两个专业名词:浅拷贝及深拷贝。

        什么是浅拷贝呢?C#在拷贝值类型时会逐位复制,但当复制引用类型时,它就开始偷懒了,只复制引用但是不复制引用对象本身,也可以理解为C++中的指针,它只存放对象的内存地址,不记录对象本身的数据。

        这时候再回过头来看看上方的代码,应该会知道为什么它运行后的结构只输出的我们最后一次赋值的内容,因为当我们拷贝出来后,对象本身指向的还是原来的内存地址,那当然在每次修改其内容时,修改的一直都是同一块内存地址所指向的内存数据。可我们不想要这种效果,我们需要它给什么内容就显示什么内容,让它变成我们想要的那种效果该怎么办呢?这时候深拷贝则闪亮登场。

        所谓深拷贝,就是将所需拷贝的数据重新用一块内存空间将其记录下来,这样再次修改时就会在复制出来的内存块中修改对象数据了。我们将上方代码以深拷贝的形式稍作修改。

/// <summary>
    /// 工作经历类,存放公司名称及工作年限,同时实现 .Net提供的ICloneable接口,用来克隆并返回自身
    /// </summary>
    class WorkExperence:ICloneable
    {
        public string CompanyName{ get; set; }
        public string Time { get; set; }

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

    /// <summary>
    /// 简历类,同时实现 .Net提供的ICloneabel接口,用来克隆并返回自身
    /// </summary>
    class Resume : ICloneable
    {
        private string name;
        private string sex;
        private string age;
        WorkExperence work;

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

        public Resume(WorkExperence work)
        {
            this.work = (WorkExperence)work.Clone();
        }

        public void SetPersonalInfo(string age,string sex)
        {
            this.age = age;
            this.sex = sex;
        }

        public void SetWorkExperence(string companyName,string time)
        {
            work.CompanyName = companyName;
            work.Time = time;
        }

        public void Display()
        {
            Console.WriteLine("{0} {1} {2} {3} {4}", name, age, sex, work.CompanyName, work.Time);
        }

        /// <summary>
        /// 实现克隆接口,返回自身
        /// </summary>
        /// <returns></returns>
        public object Clone()
        {
            //return this.MemberwiseClone();

            Resume obj = new Resume(this.work);
            obj.name = this.name;
            obj.age = age;
            obj.sex = sex;
            return obj;
        }
    }

         我们将WorkExperence类也实现ICloneable接口并返回自身,接着我们在Resume中多增加了一个构造函数,用来传入workexperence对象,最后我们将Resume类中的克隆方法改造为实例化Resume类,并将其返回。

        那么这样做的好处是什么呢?在我们克隆Resume类的时候,我们重新实例化了一个Resume类,这样就保证了克隆出来的对象里面的数据不会再指向原先存放的内存地址中的数据,而是新开辟了内存空间来存放,这样当我们设置新克隆出来的对象的WorkExperence中的数据时就不会出现之前浅拷贝所导致一改全改的情况发生,同时也实现了我们最初拷贝已经实例化的类避免类中的构造函数执行耗时的问题,同时别人在使用时也无需关心具体的克隆过程,只是将克隆出来的类继续使用。

好了以上就是原型模式的简单介绍以及使用案例,最后thanks for reading!!!!

注:本人的设计模式系列是根据大话设计模式一书中的内容,加以自身总结说明,一方面想多锻炼自身的表达能力,另一方面希望能够提升自身的编程能力及代码架构水平。若是能让更多的人通过我的文章来明白该设计模式的作用、应用场景及独立设计出来,当然也是对我个人表达能力的提升及掌握程度的认可。最后再次说明,本文内容来自大话设计模式一书,我只是写出来并加以总结希望达到熟练掌握的程度。如果你通过本文搞懂了,那么50%归功于此书,49%归功于你自己,1%为本人(脸都不要了)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值