设计模式-创建型-原型模式,纸箱?塑料袋?

206 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情

前言

原型模式介绍

对于JavaScript的开发者来说原型模式是非常常见的一种开发模式,JavaScript是一种基于原型的面向对象的编程语言,和Java、C++这类面向对象的语言有一定的区别。原型模式主要是通过拷贝原对象来创建一个新对象,平常在Android的开发过程中我个人是很少用到这个设计模式的。

简介

定义

原型模式( Prototype )隶属于创建型,原理主要是实现一个克隆当前对象方法。利用对已有的对象进行拷贝的方式,从而创建一个新的对象。

特点

当对象的创建或成员变量的创建需要发生复杂的计算、耗时才能获取时,在已有的对象上进行多次拷贝,降低了类的初始化次数,以达到节省创建的时间。

理解实现

前提

在学习理解原型模式之前,我们需要理解两个概念,它们时原型模式实现的两种方式:浅拷贝深拷贝

浅拷贝

通过拷贝创建一个新的对象,该操作当原型对象的成员变量是基本数据类型时会直接拷贝基本数据类型的值,比如Int,char这种。当原型对象的成员变量是引用类型时只会拷贝其内存地址,新的对象与原型对象共享这一部分内存,操作其中任意对象都会影响到其他的对象。

深拷贝

通过拷贝创建一个新的对象,该操作会将原型对象从堆内存中完整的复制一份出来,在堆中用新的内存地址去存放所有的类型成员变量。新的对象与原型对象不共享内存,操作其中任意对象不会影响到其他的对象。

借助他人的两张图理解一下 浅拷贝与深拷贝)

现实模型

大家都知道我有一个卖果子的老乡,老乡前段时间学了些巴啦啦小魔法,我现在买果子他可以很快给我送来,而且送来的果子都长一摸一样,唯一不同的是有时用塑料袋随便装装,有时又用纸箱包装的非常精美。我还是买完回家吃了写水文。

分析模型

主线:老乡用纸箱或塑料袋送一摸一样的水果给我,我吃完写水文。

模型1: 社区居民 -> 消费者

模型2: 老乡 -> 单例

我(消费者)

public class Me:ShuaiGe {

    public static void main(String[] args) {
         // 完成塑料袋
        PlasticWithFruit plasticWithFruit = new PlasticWithFruit("塑料袋1",new XFruit("菠萝"));
        PlasticWithFruit plasticWithFruit1 = (PlasticWithFruit) plasticWithFruit.clone();
        plasticWithFruit1.name = "塑料袋2";
        
        // 完成纸箱
        CartonWithFruit cartonWithFruit = new CartonWithFruit();
        cartonWithFruit.name = "纸箱1";
        cartonWithFruit.xFruit = new XFruit("菠萝");
        
        CartonWithFruit cartonWithFruit1 =(CartonWithFruit) cartonWithFruit.deepClone();
        cartonWithFruit1.name = "纸箱2";
        
    }
}

塑料袋 (浅拷贝)

public class PlasticWithFruit implements Cloneable{

    public String name;
    public XFruit xFruit;

    public PlasticWithFruit(String name,XFruit fruit) {
        this.name = name;
        this.xFruit = fruit;
    }

    //浅拷贝
    @Override
    protected Object clone(){
        PlasticWithFruit plasticWithFruit = null;
        try {
            plasticWithFruit = (PlasticWithFruit) super.clone();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return plasticWithFruit;
    }
}

纸箱(深拷贝)

public class CartonWithFruit implements Cloneable{

    public String name;
    public XFruit xFruit;

    public CartonWithFruit() {
        super();
    }

    //深拷贝 - 方式1 使用clone 方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object deep = null;
        //完成对基本数据类型(属性)和String的克隆
        deep = super.clone();
        //对引用类型的属性,进行单独的处理。
        CartonWithFruit cartonWithFruit = (CartonWithFruit) deep;
        cartonWithFruit.xFruit = (XFruit) xFruit.clone();

        return deep;
    }

    //深拷贝 -  方式2 通过对象序列化实现(推荐使用)
    public Object deepClone() {
        //创建流对象
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;

        try {

            //序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            //当前这个对象以对象流的方式输出
            oos.writeObject(this);

            //反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);

            CartonWithFruit copyObj = (CartonWithFruit) ois.readObject();

            return copyObj;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }
}

水果(引用类型)

public class XFruit implements Serializable, Cloneable {

    private static final long serialVersionUID = 1L;

    private String fruitName;

    public XFruit(String fruitName) {
        this.fruitName = fruitName;
    }

    //因为该类的属性,都是String,因此我们这里使用默认的clone完成即可.
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

使用实例

下面以IM中的消息作为列子进行理解,在沟通中消息对象是一个庞大且频繁调用的对象,我们在聊天室内发送消息其实不需要反复构建消息对象用于发送,这时候我们可以考虑使用原型模式对消息对象进行浅拷贝实现

消息对象

public class ChatMessage implements Cloneable{
    private String uid;
    private String message;

    public ChatMessage() {
    }

    public void setChatMessage(String uid, String message) {
        this.uid = uid;
        this.message = message;
    }

    @NonNull
    @Override
    public ChatMessage clone() {
        ChatMessage chatMessage = null;
        try {
            chatMessage = (ChatMessage) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return chatMessage;
    }

    public void sendChatMessage(){
        System.out.println("模拟发生出去的消息是:"+message);
    }
}

调用方

public class ChatRoom {
    public static void main(String[] args){
        ChatMessage chatMessage = new ChatMessage();
        chatMessage.chatMessage1("userA","A发送的消息");

        ChatMessage chatMessage1 = (ChatMessage) chatMessage.clone();
        chatMessage1.chatMessage1("userB","B发送的消息");

        chatMessage.sendChatMessage();
        chatMessage1.sendChatMessage();
    }
}

适用环境

  1. 当创建的对象比较耗时且使用频率较高时,可以利用原型模式来减小创建时间,同时也能够提高效率。
  2. 不用重新初始化对象,而是动态地获得对象运行时的状态