每日设计模式——原型模式

示例说原型模式

又到了一年一度最火热最繁忙的秋招,每天都会收到各个公司各种各样的短信或邮件,你是否层认真的思考过,公司是如何给你发送这条短信或者这封邮件的呢???

有的人可能会说,hr小姐姐们一条一条编辑的啊。但是真的是这样吗???

那有人又说了,我直接将短信的大致内容编辑成一个模板交给代理商,代理商会给我解决的,但是你有想过代理商又是怎么给你发送短信或邮件的呢??

假如这个任务交给你,你怎么来解决呢???

有人说很简单,看我的:

import java.util.Random;

public class Test {
    //发送邮件数量
    private static int MAXCOUNT = 100;

    public static void main(String[] args) {
        //定义邮件模板
        Mail mail = new Mail(new MailTemplate());
        mail.setTail("xxx公司版权所有");
        for (int i = 0; i < MAXCOUNT; i++) {
            mail.setAppellation(getRandString(5) + " 先生(女士)");
            mail.setReceiver(getRandString(5) + "@" + getRandString(3) + ".com");
            sendMail(mail);
        }
    }

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

    private static String getRandString(int maxLength) {
        String source = "qwertyuioplkjhgfdsazxcvbnmMNBVCXZASDFGHJKLPOIUYTREWQ";
        StringBuffer sb = new StringBuffer();
        Random random = new Random();
        for (int i = 0; i < maxLength; i++) {
            sb.append(source.charAt(random.nextInt(source.length())));
        }
        return sb.toString();
    }
}


class MailTemplate {
    //邮件主题
    private String mailSubject = "xxx公司招聘";
    //邮件内容
    private String mailContext = "我们是xxx互联网公司,目前公司就以下岗位非常缺人。。。。,我们期待你的加入!";

    public String getMailSubject() {
        return mailSubject;
    }

    public String getMailContext() {
        return mailContext;
    }
}

class Mail {
    //收件人
    private String receiver;
    //邮件主题
    private String subject;
    //称谓
    private String appellation;
    //邮件内容
    private String context;
    //邮件尾部
    private String tail;

    public Mail(MailTemplate template) {
        this.context = template.getMailContext();
        this.subject = template.getMailSubject();
    }

    public String getReceiver() {
        return receiver;
    }

    public String getSubject() {
        return subject;
    }

    public String getAppellation() {
        return appellation;
    }

    public String getContext() {
        return context;
    }

    public String getTail() {
        return tail;
    }

    public void setReceiver(String receiver) {
        this.receiver = receiver;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public void setAppellation(String appellation) {
        this.appellation = appellation;
    }

    public void setContext(String context) {
        this.context = context;
    }

    public void setTail(String tail) {
        this.tail = tail;
    }
}

 咋一看,嗯,还不错,基本实现了邮件发送,但是这是单个线程在运行,假如发送一封邮件需要0.02秒,那发600万封邮件需要33个小时,也就是一整天都发送不完,今天没发完,明天账单又产生了,日积月累,激起了客户的不满,那咋办??

有人说,那好办,我把sendMail修改为多线程。但是只把sendMail修改成多线程还是有问题啊,产生第一封邮件放到线程1中运行,还没有发送出去呢,线程2也启动了,直接把邮件对象mail的收件人地址和称谓改了,线程不安全了。说到线程不安全,你可能会想到加锁等等一系列解决措施,但是加锁是极其影响性能的。

那用啥方式来解决这个问题呢???这就是我们今天要说的原型模式:通过对象的复制功能来解决这个问题。先来看看代码,再来细讲。

class Mail implements Cloneable {
    //收件人
    private String receiver;
    //邮件主题
    private String subject;
    //称谓
    private String appellation;
    //邮件内容
    private String context;
    //邮件尾部
    private String tail;

    public Mail(MailTemplate template) {
        this.context = template.getMailContext();
        this.subject = template.getMailSubject();
    }

    public String getReceiver() {
        return receiver;
    }

    public String getSubject() {
        return subject;
    }

    public String getAppellation() {
        return appellation;
    }

    public String getContext() {
        return context;
    }

    public String getTail() {
        return tail;
    }

    public void setReceiver(String receiver) {
        this.receiver = receiver;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public void setAppellation(String appellation) {
        this.appellation = appellation;
    }

    public void setContext(String context) {
        this.context = context;
    }

    public void setTail(String tail) {
        this.tail = tail;
    }

    @Override
    protected Mail clone() throws CloneNotSupportedException {
        Mail mail = (Mail) super.clone();
        return mail;
    }

    @Override
    public String toString() {
        return "Mail{" +
                "receiver='" + receiver + '\'' +
                ", subject='" + subject + '\'' +
                ", appellation='" + appellation + '\'' +
                ", context='" + context + '\'' +
                ", tail='" + tail + '\'' +
                '}';
    }
}

在Mail中,我们实现了Cloneable接口,重写了clone方法。下面再来看看Test类的变化。

 public static void main(String[] args) throws CloneNotSupportedException {
        //定义邮件模板
        Mail mail = new Mail(new MailTemplate());
        mail.setTail("xxx公司版权所有");
        for (int i = 0; i < MAXCOUNT; i++) {
            Mail cloneMail = mail.clone();
            System.out.println(cloneMail);
            cloneMail.setAppellation(getRandString(5) + " 先生(女士)");
            cloneMail.setReceiver(getRandString(5) + "@" + getRandString(3) + ".com");
            sendMail(cloneMail);
        }
    }
}

运行结果不变,一样完成邮件的发送,而sendMail即使是多线程也没有关系。看main方法里边的for循环,每次我们都通过mail的clone方法,把对象复制一份,产生一个新的对象,和原有对象一样,然后再修改细节数据。这种通过对象复制来产生新对象的模式就叫原型模式。其类图结构如下:

 原型模式的核心是一个clone方法,通过该方法进行对象拷贝,java提供了一个Cloneable接口来标识这个对象是可拷贝的,为什么说是“标识”呢?因为Cloneable类中一个方法也没有,这个接口只是一个标记作用,在JVM中,只有具有这个标记的对象才有可能被拷贝。那怎么样才能从“有可能被拷贝”到“可以被拷贝”呢??方法是覆盖clone方法,即重写clone方法。

原型模式的优点

1. 原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体里边产生大量对象的时候,原型模式可以更好的体现其优点。

2. 逃避构造函数的约束,直接在内存中拷贝,构造函数是不会执行的。

原型模式使用场景

1.资源优化:类初始化需要消耗非常多的资源,这个资源包括数据、硬件资源等。

2. 性能和安全要求:通过new产生一个对象需要非常频繁的数据准备或者访问权限,则可以使用原型模式。

3. 一个对象多个修改者的场景。一个对象需要提供给其他对象访问,而各个调用者可能都需要修改其值,可以考虑使用原型模式。一般在项目中,原型模式很少单独出现,一般和工厂方法模式一起出现。

使用原型模式需要注意的问题

1.构造函数不会被执行;

2. 深拷贝和浅拷贝。关于深拷贝和浅拷贝可查看博文https://blog.csdn.net/u014727260/article/details/55003402

3.clone与final:对象的clone和对象内的final关键字是有冲突的,final类型的数据是不支持重新赋值的,要实现深拷贝就需要去除final关键字,如图所示:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值