• 设计模式之原型模式(其实就是一个克隆)


    原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

    吐槽在先:与其说这是一个模式,倒不如说是一种解决对象克隆的方法。好吧,还是叫做模式专业一点。

    场景:我们经常会在代码中使用Clone方法进行对象的克隆,当然我们的类默认是木有这个方法,需要自己实现自ICloneable接口,然后就可以开始自己的克隆了。当然,中间不仅仅是实现这个简单,克隆还分为浅拷贝和深拷贝,等会再说。

    模式元素:这个其实真没什么元素,只要你的类实现了iCloneable接口就可以的哦(当然中间涉及到的还是深拷贝和浅拷贝问题哦)。

    开始实战:

    我们用写简历这个例子来作为我们模式的探讨对象,简历可能会写很多份,因为我们发送多次。

      /// <summary>
        /// 简历类
        /// </summary>
        class Resume:ICloneable
        {
            public string Name{get;set;}
            public string Sex { get; set; }
            public int Age { get; set; }
            public string Address { get; set; }
            public string TimeArea { get; set; }
            public string Company { get; set; }
    
            /// <summary>
            /// 设置工作经历
            /// </summary>
            /// <param name="timeArea"></param>
            /// <param name="company"></param>
            public void SetWorkExperience(string timeArea,string company)
            {
                this.TimeArea = timeArea;
                this.Company = company;
            }
    
            /// <summary>
            /// 显示个人信息
            /// </summary>
            public void Show()
            {
                Console.WriteLine(string.Format( "姓名:{0},性别:{1},年龄:{2}",Name,Sex,Age));
                Console.WriteLine(string.Format("工作经历:{0} {1}",TimeArea,Company));
            }
    
            /// <summary>
            /// 克隆方法
            /// </summary>
            /// <returns></returns>
            public object Clone()
            {
                return (Resume)this.MemberwiseClone();
            }
        }

     实体类很简单,除了几个简单的属性之外还有一个设置工作经历的方法,一个显示个人信息的方法,当然还有我们的克隆方法。

    调用如下:

             Resume resumeA = new Resume { Name = "Listen", Age = 23, Address = "中国" };
                resumeA.SetWorkExperience("2010--2012", "X公司");
    
                Resume resumeB = (Resume)resumeA.Clone();
                resumeB.SetWorkExperience("2012--2014", "Y公司");
    
                Resume resumeC = (Resume)resumeB.Clone();
                resumeC.SetWorkExperience("2014--2016", "Z公司");
    
                resumeA.Show();
                resumeB.Show();
                resumeC.Show();
                Console.Read();

    调用过程也很简单,实例化了三个Resume对象,哦no,是一个对象,然后我们克隆了两份才对,当然要通过我们的Clone方法进行,最后进行展示数据,效果上来:

     

    可以看到除了每次的工作经历不一样,其余的个人信息都是相同的,这就是克隆的好处,他把我们的属性的值都给拷贝了一份(等等此处有问题,不是所有的属性哦,只是值类型而已),赋值给新的对象。

    P.S:早就想解释了,this.MemberwiseClone()此方法是干嘛呢,问的好,此方法就是创建当前对象的浅表副本,也就是浅复制。方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型,则直接复制字段的值。如果字段是引用类型,则复制引用(也就是只new一个对象)而不复制引用的对象(也就是不赋值引用对象的各个属性);因此原始对象及其副本引用同一个对象。

    上述的例子中的属性均为值类型,所以进行克隆是没问题的,现在需求有了改变,将工作经历提取为一个类,作为简历的一个属性存在,如下:

     添加工作经历类:

    /// <summary>
        /// 工作经历
        /// </summary>
        class WorkExperience
        {
            private string _workDate;
    
            public string WorkDate
            {
                get { return _workDate; }
                set { _workDate = value; }
            }
            private string _company;
    
            public string Company
            {
                get { return _company; }
                set { _company = value; }
            }
        }

    修改简历类:

     /// <summary>
        /// 简历类
        /// </summary>
        class Resume:ICloneable
        {
            public string Name{get;set;}
            public string Sex { get; set; }
            public int Age { get; set; }
            public string Address { get; set; }
            public WorkExperience WorkExperience;
    
            public Resume()
            {
                this.WorkExperience = new WorkExperience();
            }
    
            /// <summary>
            /// 设置工作经历
            /// </summary>
            /// <param name="timeArea"></param>
            /// <param name="company"></param>
            public void SetWorkExperience(string timeArea,string company)
            {
                WorkExperience.WorkDate = timeArea;
                WorkExperience.Company = company;
            }
    
            /// <summary>
            /// 显示个人信息
            /// </summary>
            public void Show()
            {
                Console.WriteLine(string.Format( "姓名:{0},性别:{1},年龄:{2}",Name,Sex,Age));
                Console.WriteLine(string.Format("工作经历:{0} {1}", WorkExperience.WorkDate, WorkExperience.Company));
            }
    
            /// <summary>
            /// 克隆方法
            /// </summary>
            /// <returns></returns>
            public object Clone()
            {
                return (Resume)this.MemberwiseClone();
            }
        }

    添加了一个工作经历类,属性还是老样子,同时修改了Resume简历类,删除了工作时间和公司名称,替换的为工作经历类的一个对象;在构造函数中对工作经历类进行初始化,同时修改了设置工作经历中的代码以及显示个人信息的代码,其余木有变化哦。

    再次使用:

    使用的代码不用变哦.

    我勒个去,嘛回事,为神马工作经历都是同一个呢,明明设置了三次,哦啦啦,为神马捏。还是因为MemberwiseClone搞的鬼,刚才说了它复制值类型是直接复制的,而复制引用类型而只是赋值一个引用(即复制一个地址,也就是说我们三个对象的工作经历是同一个地址的对象,我的天呢),所以它记录的永远都是最后一次赋值的结果,好恐怖哦。所以,我们就需要一个新的东西,等等等等,就是“深复制”。

    继续修改:

    工作经历类:

     /// <summary>
        /// 工作经历
        /// </summary>
        class WorkExperience : ICloneable
        {
            private string _workDate;
    
            public string WorkDate
            {
                get { return _workDate; }
                set { _workDate = value; }
            }
            private string _company;
    
            public string Company
            {
                get { return _company; }
                set { _company = value; }
            }
    
            public object Clone()
            {
                return (WorkExperience)this.MemberwiseClone();
            }
        }

    给工作经历类同样实现了ICloneable接口,并且实现了Clone方法,可是实现了这个方法谁来调用呢,问的好,继续修改。

    简历类修改:

     /// <summary>
            /// 克隆方法
            /// </summary>
            /// <returns></returns>
            public object Clone()
            {
                Resume resume = new Resume();
                resume.Name = this.Name;
                resume.Sex = this.Sex;
                resume.Age = this.Age;
                resume.WorkExperience = (WorkExperience)this.WorkExperience.Clone();
                return resume;
            }

    再次调用:

    同样不需修改。

    哦好开心,因为简历类只修改了Clone方法,在克隆的时候手动new一个对象,然后把乱七八糟的属性一一赋值,最后把工作经历类通过自己的克隆得到,然后返回克隆的简历对象,这样既正确了哦。

    克隆无处不在,比如DataSet既有Copy方法,也有Clone方法,Clone只会复制结构,而Copy则会把结构和数据都给复制过去。

     好了,这次就到这里了,源码在此,欢迎大家多提意见和建议。

  • 相关阅读:
    服务限流原理及算法
    Kafka 与 RabbitMQ 如何选择使用哪个?
    分布式事务之最终一致性实现方案
    如何在Ubuntu 14.04中使用systemctl?
    postgresql13 for window 安装及备份还原数据
    手把手教大家在mac上用VMWare虚拟机装Ubuntu
    在Mac平台上使用Multipass安装Ubuntu虚拟机
    如何在markdown中插入js和css
    HTML5标签
    linux
  • 原文地址:https://www.cnblogs.com/ListenFly/p/3225873.html
Copyright © 2020-2023  润新知