美文网首页模型与算法
设计模式-原型模式

设计模式-原型模式

作者: NealLemon | 来源:发表于2019-08-06 07:45 被阅读24次

定义

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

特点

不需要知道任何创建细节,不调用构造函数。

适用场景

  • 类初始化消耗较多资源。
  • new 产生的一个对象需要非常繁琐的过程(数据准备,访问权限等)。
  • 构造函数比较复杂的时候。
  • 循环体中生产大量的对象时。

优点

  • 性能比new 略高
  • 简化创建过程

缺点

  • 必须配备克隆方法

  • 对克隆复杂对象或对克隆出的对象进行改造时,容易引入危险(有深拷贝和浅拷贝相关的BUG)。

深拷贝与浅拷贝

简单点说 假设 A对象中一个属性为B,当A进行拷贝生成A1时同时A1里的属性为B1。但是当修改B时,B1跟着修改,这就是浅拷贝。

代码

这里我们就以发送邮件的逻辑为基础,做一个展示。

首先定义邮件类Mail
public class Mail implements Cloneable{

    private String name;

    private String content;

    private String address;

    //默认当前时间 由于是对象,在clone的时候会有深浅拷贝的问题
    private Date sendDate = new Date();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public Date getSendDate() {
        return sendDate;
    }

    public void setSendDate(Date sendDate) {
        this.sendDate = sendDate;
    }

    @Override
    public String toString() {
        return "Mail{" +
                "name='" + name + '\'' +
                ", content='" + content + '\'' +
                ", address='" + address + '\'' +
                ", sendDate=" + sendDate +
                '}' + super.toString();
    }

    //注意这里,会涉及到深浅拷贝的问题
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Mail cloneMail = (Mail) super.clone();
        return cloneMail;
    }
}

这里我故意写了一个Date类型的属性,这样我们会更容易看出深浅拷贝BUG。

邮件发送类MailUtil
public class MailUtil {

    private static String CONTENT = "向{0},邮件地址:{1},发送内容: {2},发送时间:{3},发送成功!";
    public  static void sendMail(Mail mail) {
        System.out.println(MessageFormat.format(CONTENT,mail.getName(),mail.getAddress(),mail.getContent(),mail.getSendDate()));
    }
}

我们可以看到就是简单的输出。

发送启动类
public class PrototypeBootStrap {

    public static void main(String[] args) throws CloneNotSupportedException, InterruptedException {
        Mail mail = new Mail();
        mail.setName("钢铁侠");
        mail.setContent("我是你的粉丝");
        mail.setAddress("复仇者联盟");
        Mail cloneMail = (Mail) mail.clone();
        MailUtil.sendMail(mail);
        MailUtil.sendMail(cloneMail);
        //穿越一下
        mail.getSendDate().setTime(99999999999L);
        MailUtil.sendMail(mail);
        MailUtil.sendMail(cloneMail);
    }
}

我们看到 在第一次发送邮件之后,不小心穿越一下,设置了原对象(mail)的时间。正常我们理解既然cloneMailmail的拷贝。那么原对象修改,拷贝的对象里的值是不会变得。但是结果是这样吗?我们运行一下。

prototypecopy.jpg

这里我们就发现了浅拷贝的问题了。也是BUG率最容易出现的地方。

改进邮件类Mail

我们将邮件类中的 clone()方法改进一下。

@Override
protected Object clone() throws CloneNotSupportedException {
    Mail cloneMail = (Mail) super.clone();
    //避免浅拷贝
    cloneMail.sendDate = (Date) cloneMail.sendDate.clone();
    return cloneMail;
}

我们再来运行一下启动类。

prototypecopy1.jpg

这次的输出结果却是是我们想要的。

小结

在运用原型模式的时候,一定要多加小心,避免不必要的麻烦。希望我们可以在设计模式的海洋里找到属于自己的那片蓝。

相关文章

网友评论

    本文标题:设计模式-原型模式

    本文链接:https://www.haomeiwen.com/subject/myxbdctx.html