《一天一模式》— 原型模式

[亡魂溺海] 提交于 2020-08-16 09:29:33

一、原型模式的概念

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

二、什么时候使用原型模式

原型模式可以理解为:以一个类作为模板,克隆出无数个类,克隆出来的类与模板就再无关联。

以下场景可以使用原型模式:

  • 构造函数较复杂,或者不想使用构造函数创建对象;
  • 资源优化,在初始化时一个类时需要较多的资源,使用原型模式,初始化一次后,就进行克隆(不会再初始化);
  • 性能提升,使用克隆要比使用new创建一个对象的性能高,克隆直接在内存中操作二进制流,new则需要JVM做很多的准备(加载、验证、准备、解析、初始化),所以在某些场景要大量创建相同对象时,可以节约性能;

下面就以提升性能作为场景,来用Java实现原型模式。

三、怎么使用原型模式

3.1 实现方式

首先假设一个场景,中秋节将至,系统要为全部的500万(或者更多)用户发送消息(一条条发,先不考虑其他技术实现,主要为了说明原型模式)。这种情况下,如果贸然使用for循环来通过new关键字创建500万个对象,假设new一个对象要花费0.04秒,那么new500万个也未必能在一天之内发送完毕。

这种情况下就可以使用原型模式来提升性能,下面看类图和代码:

代码如下:

// 消息模板,继承Cloneable接口
public interface MessageTemplate extends Cloneable {

    // 克隆消息
    MessageTemplate cloneMessage() throws CloneNotSupportedException;

}

// 消息
public class Message implements MessageTemplate {

    // 实现克隆方法
    public Message cloneMessage() throws CloneNotSupportedException {
        Message message;
        message = (Message) clone();
        return message;
    }

    @Override
    public String toString() {
        return "Message{" +
                "createTime=" + createTime +
                ", title='" + title + '\'' +
                ", context='" + context + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }

    // getter and setter ...

    private Date createTime;
    private String title;
    private String context;
    private String phone;


}

// 使用克隆模式
public class Client {

    public static void main(String[] args) throws CloneNotSupportedException {

        // 模拟发送消息,加入发送100万个消息给不同用户
        // 消息模板使用相同模板,消息内容有某些不同
        // 则可以使用原型模式

        int count = 100; // 用户数量,就不写100万了
        // 创建模板
        Message message = new Message();
        message.setTitle("节日关怀");
        message.setContext("尊敬的%s,您好。吧啦吧啦……");
        for (int i = 0; i < count; i++) {
            Message m = message.cloneMessage();
            m.setContext(String.format(message.getContext(), i));
            m.setPhone(i + "");
            m.setCreateTime(new Date());
            System.out.println(m.toString());
        }

    }

}

// 输出
Message{createTime=Tue May 19 23:05:52 CST 2020, title='节日关怀', context='尊敬的0,您好。吧啦吧啦……', phone='0'}
Message{createTime=Tue May 19 23:05:52 CST 2020, title='节日关怀', context='尊敬的1,您好。吧啦吧啦……', phone='1'}
Message{createTime=Tue May 19 23:05:52 CST 2020, title='节日关怀', context='尊敬的2,您好。吧啦吧啦……', phone='2'}
......
Message{createTime=Tue May 19 23:05:52 CST 2020, title='节日关怀', context='尊敬的98,您好。吧啦吧啦……', phone='98'}
Message{createTime=Tue May 19 23:05:52 CST 2020, title='节日关怀', context='尊敬的99,您好。吧啦吧啦……', phone='99'}

上面的代码就是原型模式的做法和用法。

核心就是使用了Java中Object对象自带的clone方法,来完成类的克隆。

这里需要注意的是Cloneable接口,它比较特别,clone方法并不是它定义的,它只是用来描述对象可以使用clone方法,是一种标记接口。

3.2 原型模式的好处

原型模式的好处,就体现他的使用场景:

  • 构造函数较复杂,或者不想使用构造函数创建对象;
  • 资源优化,在初始化时一个类时需要较多的资源;
  • 性能提升,使用克隆要比使用new创建一个对象的性能高;

3.3 注意事项

原型模式的注意事项,主要强调两点:

忽略构造函数:使用clone()方法,不会触发对象的构造函数,如果类需要在构造函数中进行一些业务,则需要注意;

深拷贝:clone()方法会复制基本类型,但是引用类型复制的是引用的内存地址(指针),所以带有引用类型的对象进行clone()时,需要处理深拷贝,网上有很多文章,就不在此叙述了;

四、总结

总的来说,原型模式属于比较简单的设计模式之一,也比较好理解,主要理清2点就能比较好的应用它:

使用场景:在什么时候适合用原型模式来创建对象;

技术实现:涉及到了使用cloneable接口,使用Object对象的clone方法,以及注意深拷贝

以上就是我对原型模式的一些理解,有不足之处请大家指出,谢谢。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!