设计模式学习之原型分身术(原型模式)

原型模式的定义:        

        分身术说的其实就是设计模式中的原型模式。原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。听着和神仙剧的中的分身术十分相似。

如何使用原型模式:

需求:某银行举办清凉一夏抽奖活动,需要通过邮件的形式通知到客户

设计思路:邮件内容基本保持一致,主要区别是收件人。活动内容不定期改变,定义成活动模板。为了保证线程安全和消息准确性每发一次邮件就需要新建一个邮件内容类。类图设计:

代码实现:

package com.specify;

/**
 * 原型模式
 * 邮件类
 */
public class Mail implements Cloneable{
    //收件人
    private String receiver;
    //邮件名称
    private String subject;
    //称呼
    private String appllation;
    //邮件内容
    private String contxt;
    //邮件的尾部
    private String tail;
    public Mail( AdvTemlate advTemlate){
        this.contxt = advTemlate.getAdvContext();
        this.subject = advTemlate.getAdvSubject();
    }

    @Override
    public  Mail clone(){
        Mail mail = null;
        try {
             mail = (Mail)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return mail;
    }

  //get/set方法...... 
}

/**
 * 原型模式
 * 广告模板
 */
public class AdvTemlate {
    //广告信名称
    private String advSubject = "XX银行清凉一夏信用卡抽奖活动";
    //广告信内容
    private String advContext ="清凉一夏信用卡抽奖活动,只要刷卡机就有获取百万机会";

    public String getAdvSubject() {
        return this.advSubject;
    }

      public String getAdvContext() {
        return this.advContext;
    }

}


package com.specify;

import java.util.Random;

/**
 * 原型模式
 * 客户端
 */
public class Client {
    //发送活动次数
    private static  int MAX_COUNT = 6;

    public static void main(String[] args) {
        //模拟发送邮件
        int i = 0 ;
        //定义模板
        Mail mail = new Mail(new AdvTemlate());
        mail.setTail("某某银行版本所有");
        while (i<MAX_COUNT){
            Mail cloneMail = mail.clone();
            cloneMail.setAppllation(getRandString(5) + "先生(女士)");
            cloneMail.setReceiver(getRandString(5) + "@"+getRandString(8));
            sendMail(cloneMail);
            i++;
        }
    }
    //获得指定长度的随机字符串
    public static String getRandString(int length){
        String sounce = "abcdefghijklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ";
        StringBuffer sb = new StringBuffer();
        Random random = new Random();
        for (int i = 0; i <length ; i++) {
            sb.append(sounce.charAt(random.nextInt(sounce.length())));
        }
        return sb.toString();
    }

    //发送邮件
    public static  void sendMail(Mail mail){
        System.out.println("标题 : "+mail.getSubject()+"\t收件人" +mail.getReceiver()+"\t发送成功");
    }

}

执行结果:
标题 : XX银行清凉一夏信用卡抽奖活动	收件人OmScx@dzmCWkkB	发送成功
标题 : XX银行清凉一夏信用卡抽奖活动	收件人PTwnk@swMvYYGt	发送成功
标题 : XX银行清凉一夏信用卡抽奖活动	收件人KaDGh@VGTUSlpu	发送成功
标题 : XX银行清凉一夏信用卡抽奖活动	收件人uWDCj@bGtwGTcn	发送成功
标题 : XX银行清凉一夏信用卡抽奖活动	收件人YOiUd@BVuvyKdE	发送成功
标题 : XX银行清凉一夏信用卡抽奖活动	收件人hNgoD@ocpKBroY	发送成功

原型模式所以然:

        从代码中,我们可以看出,在生成邮件实例的过程中,使用的是重写Object的clone实现的。这也是原型模式最核心的地方。生成对象通过clone生成而不是通过new一个实例。效果是一样,但是产生对象的过程却有很大差别。clone是在内存上直接进行二进制拷贝,要比new一个对象的性能要好的多,尤其是对于需要循环产生大量对象的场景。

 clone产生的对象和new实例在某些场景下还是会有一定的区别:

        首先clone是区别深拷贝和浅拷贝,Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝。和浅拷贝相对的也就是深拷贝。在引用对象中引用的成员变量必须满足两个条件才不会被拷贝:一是类的成员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是一个原始类型或不可变对象。所以需要注意在拷贝对象时是否要涉及深拷贝,深拷贝可以使用将成员变量或者引用对象进行独立拷贝的方式,不过使用时一定要谨慎。参考内容:Java深拷贝和浅拷贝详解

        其次是进行clone时,构造方法不会被执行,这也是和对象产生的过程有关系,如果构造函数有约束的情况下不建议适用。

        最后clone和final的恩怨,final修饰的变量是无法修改的,所以clone和final关键字冲突,要使用clone方法,类的成员变量上不要增加final关键字。

原型模式总结:

性能优势:相对于new一个对象,clone存在性能上的优势,尤其是产生大量对象时;

减少约束:不执行构造函数,减少了构造函数的约束,但是同时没有了约束,有时也会是劣势;

适用场景:一个对象需要多次被修改,而且仅仅是修改其中的状态值等;或者是上面的示例代码需要产生大量对象的场景。

参考《设计模式之禅》秦小波著

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值