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

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

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

场景:我们经常会在代码中使用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则会把结构和数据都给复制过去。

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

posted @ 2013-07-30 19:53  wangyafei_it  阅读(389)  评论(0编辑  收藏  举报