原型模式

21 篇文章 0 订阅
18 篇文章 3 订阅

定义:通过复制现有实例来创建新的实例,无需知道相应类的信息。

简单地理解,其实就是当需要创建一个指定的对象时,我们刚好有一个这样的对象,但是又不能直接使用,我会clone一个一毛一样的新对象来使用;基本上这就是原型模式。关键字:Clone。

1 深拷贝和浅拷贝

浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。

深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。clone明显是深复制,clone出来的对象是是不能去影响原型对象的

2 原型模式的结构和代码示例(Java)

Client:使用者

Prototype:接口(抽象类),声明具备clone能力,例如java中得Cloneable接口

ConcretePrototype:具体的原型类

可以看出设计模式还是比较简单的,重点在于Prototype接口和Prototype接口的实现类ConcretePrototype。原型模式的具体实现:一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法。
 

public class Prototype implements Cloneable {  
     public Object clone() throws CloneNotSupportedException { 
         //此句中的super.clone()是重点 
         Prototype proto = (Prototype) super.clone();   
         return proto;  
     }  
}  

 

举例(银行发送大量邮件,使用clone和不使用clone的时间对比):我们模拟创建一个对象需要耗费比较长的时间,因此,在构造函数中我们让当前线程sleep一会

public static void main(String[] args) {
              int i = 0;
              int MAX_COUNT = 10;
              EventTemplate et = new EventTemplate("9月份信用卡账单", "国庆抽奖活动...");
              long start = System.currentTimeMillis();
              while (i < MAX_COUNT) {
                     // 以下是每封邮件不同的地方
                     Mail mail = new Mail(et);
                     mail.setContent(getRandString(5) + ",先生(女士):你的信用卡账单..." + mail.getTail());
                     mail.setReceiver(getRandString(5) + "@" + getRandString(8) + ".com");
                     // 然后发送邮件
                     sendMail(mail);
                     i++;
              }
              long end = System.currentTimeMillis();
              System.out.println("用时:" + (end - start));
       }

 

用时:10001

使用clone,发送十个邮件

    public static void main(String[] args) {
              int i = 0;
              int MAX_COUNT = 10;
              EventTemplate et = new EventTemplate("9月份信用卡账单", "国庆抽奖活动...");
              long start=System.currentTimeMillis();
              Mail mail = new Mail(et);         
              while (i < MAX_COUNT) {
                     Mail cloneMail = mail.clone();
                     mail.setContent(getRandString(5) + ",先生(女士):你的信用卡账单..."
                                  + mail.getTail());
                     mail.setReceiver(getRandString(5) + "@" + getRandString(8) + ".com");
                     sendMail(cloneMail);
                     i++;
              }
              long end=System.currentTimeMillis();
              System.out.println("用时:"+(end-start));
       }

 

3 .Net中的深拷贝与浅拷贝

.net提供了一个ICloneable接口,该接口下有一个Clone()方法,你可以实现它用来实现你自己的克隆方式,比如深克隆或是浅克隆,MemberwiseClone()是object类中的一个方法,用来实现类的浅克隆。

MemberwiseClone 方法创建一个浅表副本,方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。深拷贝,即实现ICloneable接口.ICloneable可用于深拷贝和浅拷贝。

下面是一个深拷贝与浅拷贝的Model,仅供参考

[Serializable]
    public class InvoiceDetailResponse : IDeepCopy, IShallowCopy
    {
         
        public Guid merchant_id { get; set; }
        /// <summary>
        /// 名称
        /// </summary>
        public string uname { get; set; }
       
        /// <summary>
        /// 浅拷贝
        /// </summary>
        /// <returns></returns>
        public object ShallowCopy()
        {
            return this.MemberwiseClone();
        }
       /// <summary>
        /// 深拷贝 【不建议使用二进制流方法,此方法即使在类前面加了可序列化标志,调用该方法时也会报未序列化错误】,推荐使用反射方式
        /// </summary>
        /// <returns></returns>
        public object DeepCopy()
        {
            using (MemoryStream stream = new MemoryStream())
            {
                BinaryFormatter bFormatter = new BinaryFormatter();
                bFormatter.Serialize(stream, this);
                stream.Seek(0, SeekOrigin.Begin);
                return (InvoiceDetailResponse)bFormatter.Deserialize(stream);
            }
        }
 


    }
    /// <summary>
    /// 深拷贝接口
    /// </summary>
    interface IDeepCopy
    {
        object DeepCopy();
    }

    /// <summary>
    /// 浅拷贝接口
    /// </summary>
    interface IShallowCopy
    {
        object ShallowCopy();
    }

利用反射实现的深拷贝:

利用反射实现

public static T DeepCopyByReflection<T>(T obj)
{
  if (obj is string || obj.GetType().IsValueType)
  return obj;

  object retval = Activator.CreateInstance(obj.GetType());
  FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static|BindingFlags.Instance);
  foreach(var field in fields)
  {
    try
    {
      field.SetValue(retval, DeepCopyByReflection(field.GetValue(obj)));
    }
    catch { }
  }

  return (T)retval;
}

 ICloneable接口实现示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 原型模式
{
    class Helper:ICloneable
    {
        public int age { get; set; }
        public string name { get; set; }

        public Helper(int Age,string Name)
        {
            this.age = Age;
            this.name = Name;
        }

        //实现Clone
        public object Clone()
        {
            //浅复制,值类型与string(因不可变性)是直接产生一个新的副本,引用类型仅仅只复制当前实例对象的引用,结果对象与当前对象指向同一目标
            return MemberwiseClone();
        }
    }

    class FaShi : ICloneable
    {
        public Helper helper { get; set; }

        public FaShi(Helper hlp)
        {
            this.helper = hlp;
        }


        public object Clone()
        {
            //深复制,值类型与string(因不可变性)是直接产生一个新的副本,引用类型产生新的对象,与原对象实例无关
            return new FaShi(this.helper.Clone() as Helper);

            //浅复制,值类型与string(因不可变性)是直接产生一个新的副本,引用类型仅仅只复制当前实例对象的引用,结果对象与当前对象指向同一目标
             //return this.MemberwiseClone();
        }

    }

    class Program
    {
        static void Main(string[] args)
        {
            Helper h1 = new Helper(12, "张三");

            //Helper里的值类型与string是直接产生一个新的副本,所以是深复制
            Helper h2 = h1.Clone() as Helper;

            h2.age = 18;
            Console.WriteLine("姓名:{0},年纪:{1}",h1.age,h1.name);
            Console.WriteLine("姓名:{0},年纪:{1}", h2.age,h2.name);

            Console.WriteLine("=====================================");

            Helper h3 = new Helper(50, "李四");
            FaShi fs = new FaShi(h3);

            //开始复制,使用MemberwiseClone浅复制,深复制自定义实现
            FaShi fs1 = fs.Clone() as FaShi;
            fs1.helper.age = 29;

            Console.WriteLine("姓名:{0},年纪:{1}", fs.helper.age, fs.helper.name);
            Console.WriteLine("姓名:{0},年纪:{1}", fs1.helper.age, fs1.helper.name);

            Console.Read();




        }
    }
}

3 总结

原型模式的本质就是clone,可以解决构建复杂对象的资源消耗问题,能再某些场景中提升构建对象的效率;还有一个重要的用途就是保护性拷贝,可以通过返回一个拷贝对象的形式,实现只读的限制,Java与.Net中适用(拷贝比新建实例快得多)。

说明:部分内容节先自以下站点

https://blog.csdn.net/A1342772/article/details/91349142 https://www.cnblogs.com/personblog/p/11308831.html

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值