设计模式之原型模式

原型模式

  • 定义:指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
  • 不需要知道任何创建的细节,不需要调用构造函数
  • 类型:创建型

适用的场景

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

另外在软件系统中,有些对象的创建过程较为复杂,而且有时候需要频繁创建,原型模式通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象,这就是原型模式的意图所在。

优点与缺点

优点:

  • 原型模式性能比直接new一个对象性能搞
  • 简化创建过程

clone通过复制对象的二进制流的方式来复制一个对象,因此免去了new对象时比较繁重的一些操作。
但克隆的方式有时会留下坑,这就是深克隆与浅克隆的区别,后面会举例子说明。
浅克隆是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。
深克隆不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象

缺点:

  • 必须配备克隆方法(java cloneable接口)
  • 对克隆复杂对象或者对克隆出的对象进行复杂改造,容易引入风险。

代码实现原型模式

要让对象具备克隆的能力,需要让类实现Cloneable接口,并重写Object类的clone方法。

//邮件实体类
public class Mail implements Cloneable {
    private String name;
    private String emailAddress;
    private String content;

    public Mail() {
        System.out.println("Mail Class Constructor");
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        System.out.println("clone mail object");
        return super.clone();
    }
}

//发送邮件的工具类
public class MailUtil {
    public static void sendMail(Mail mail) {
        String outputContent = "向{0}同学,邮件地址{1},邮件内容:{2}发送邮件。";
        System.out.println(MessageFormat.format(outputContent, mail.getName(), mail.getEmailAddress(), mail.getContent()));
    }

    public static void saveOriginMail(Mail mail) {
        System.out.println("存储originMail记录,origin Mail:"+mail.getContent());
    }
}

//test
 public static void main(String[] args) throws CloneNotSupportedException {
        Mail mail = new Mail();
        mail.setContent("初始化模板");
        for (int i = 0; i < 10; i++) {
            Mail tmp = (Mail) mail.clone();
            mail.setName("姓名"+i);
            mail.setEmailAddress("姓名"+i+"@qq.com");
            mail.setContent("恭喜你,中奖了!");
            MailUtil.sendMail(tmp);
        }
        MailUtil.saveOriginMail(mail);
    }

image.png
以上就完成一个简单的邮件克隆的demo。后面这个例子会演示浅克隆以及深克隆对程序的影响。

深克隆与浅克隆

public class Pig implements Cloneable {
    private String name;
    private Date birthday;

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Pig(String name, Date birthday) {
        this.name = name;
        this.birthday = birthday;
    }

    //浅拷贝
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

//test
 public static void main(String[] args) throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Date birthday = new Date(0L);
        Pig pig1 = new Pig("佩奇", birthday);
        Pig pig2 = (Pig) pig1.clone();
        birthday.setTime(System.currentTimeMillis());
        System.out.println("pig1 = " + pig1);
        System.out.println("pig2 = " + pig2);
    }

image.png
如果我们的本意是,希望复制出来的对象是在内存中独立的两个对象。那上面的这个克隆就出大问题了。很明显这就是一个浅克隆带来的问题。
问题就处在Pig类里的Date,这是个引用类型,在浅拷贝中,并不会对指针所指向的对象进行复制,而是复制了引用,实际的对象还是原来的那个对象。
我们可以使用debug追踪一下:
image.png
很明显,birthday引用指向的都是同一个对象。深拷贝也很简单,就是对引用类型也进行一次克隆:

 @Override
    protected Object clone() throws CloneNotSupportedException {
        Pig pig = (Pig) super.clone();
        Date date = (Date) pig.getBirthday().clone();
        pig.setBirthday(date);
        return pig;
    }

image.png
在使用原型模式,要合理地使用深拷贝和浅拷贝,避免踩坑!!!

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值