设计模式之原型模式

原型模式定义

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

通用类图

原型模式

具体实现

原型模式不是通过new关键字来产生一个对象,而是通过对象复制来实现的。Java提供了一个Cloneable接口来标示这个对象是可拷贝的,并且类重写了clone()方法,在JVM中才能被拷贝。当调用clone方法时构造函数不会被执行。Object类的clone方法的原理是从内存中(具体地说就是堆内存)以二进制流的方式进行拷贝,重新分配一个内存块。

下面就来看看具体的代码实现:

接口Prototype继承了Cloneable接口:

package com.yrs.prototype;

/**
 * @Author: yangrusheng
 * @Description:
 * @Date: Created in 18:48 2018/7/22
 * @Modified By:
 */
public interface Prototype extends Cloneable{
    Prototype clone();
    void setValue(String str);
    Object getValue();
}

类ConcreteShallowPrototype 实现了Prototype接口:

package com.yrs.prototype;

import java.util.ArrayList;

/**
 * @Author: yangrusheng
 * @Description:
 * @Date: Created in 18:54 2018/7/22
 * @Modified By:
 */
public class ConcreteShallowPrototype implements Prototype {

    private ArrayList<String> arrayList = new ArrayList<String>();

    public ConcreteShallowPrototype() {
        System.out.println("shallow clone constructor execute");
    }

    @Override
    public ConcreteShallowPrototype clone() {
        ConcreteShallowPrototype prototype = null;
        try {
            //浅拷贝
            prototype = (ConcreteShallowPrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            //异常处理
            e.printStackTrace();
        }
        return prototype;
    }

    //设置HashMap的值
    public void setValue(String value) {
        this.arrayList.add(value);
    }

    //取得arrayList的值
    public ArrayList<String> getValue() {
        return this.arrayList;
    }
}

类ConcreteDeepPrototype 实现了Prototype接口:

package com.yrs.prototype;

import java.util.ArrayList;

/**
 * @Author: yangrusheng
 * @Description:
 * @Date: Created in 18:54 2018/7/22
 * @Modified By:
 */
public class ConcreteDeepPrototype implements Prototype {

    private ArrayList<String> arrayList = new ArrayList<String>();

    public ConcreteDeepPrototype() {
        System.out.println("deep clone constructor execute");
    }

    @Override
    public ConcreteDeepPrototype clone() {
        ConcreteDeepPrototype prototype = null;
        try {
            //深拷贝
            prototype = (ConcreteDeepPrototype) super.clone();
            prototype.arrayList = (ArrayList<String>) this.arrayList.clone();
        } catch (CloneNotSupportedException e) {
            //异常处理
            e.printStackTrace();
        }
        return prototype;
    }

    //设置HashMap的值
    public void setValue(String value) {
        this.arrayList.add(value);
    }

    //取得arrayList的值
    public ArrayList<String> getValue() {
        return this.arrayList;
    }
}

类Client:

package com.yrs.prototype;

/**
 * @Author: yangrusheng
 * @Description:
 * @Date: Created in 19:04 2018/7/22
 * @Modified By:
 */
public class Client {

    public static void main(String[] args) {
        Prototype shallowPrototype = new ConcreteShallowPrototype();
        shallowPrototype.setValue("yrs");
        Prototype copyShallowPrototype = shallowPrototype.clone();
        System.out.println(shallowPrototype.getValue());
        System.out.println(copyShallowPrototype.getValue());
        copyShallowPrototype.setValue("ByrsH");
        System.out.println(shallowPrototype.getValue());
        System.out.println(copyShallowPrototype.getValue());

        System.out.println("**************************");

        Prototype deepPrototype = new ConcreteDeepPrototype();
        deepPrototype.setValue("yrs");
        Prototype copyDeepPrototype = deepPrototype.clone();
        System.out.println(deepPrototype.getValue());
        System.out.println(copyDeepPrototype.getValue());
        copyDeepPrototype.setValue("ByrsH");
        System.out.println(deepPrototype.getValue());
        System.out.println(copyDeepPrototype.getValue());
    }
}

程序运行结果:

shallow clone constructor execute
[yrs]
[yrs]
[yrs, ByrsH]
[yrs, ByrsH]
**************************
deep clone constructor execute
[yrs]
[yrs]
[yrs]
[yrs, ByrsH]

由上面的程序运行结果可以看出执行clone()方法时,并没有执行类的构造函数。你同时也会发现两个类型的对象调用clone()后产生的对象,操作成员变量的结果是不同的。这就牵扯到浅拷贝和深拷贝的问题,Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,但其他的原始类型比如int、long、char等都会被拷贝,String类型也会被拷贝,这种拷贝就叫做浅拷贝。是一种非常不安全的方式。ConcreteDeepPrototype 类实现了深拷贝,通过调用成员变量的clone方法来实现,两个对象之间的修改互不影响。还有一种方式可以实现深拷贝:通过二进制流来操作对象,然后实现对象的深拷贝。

二进制流实现深拷贝:

package com.yrs.prototype;

import java.io.*;
import java.util.ArrayList;

/**
 * @Author: yangrusheng
 * @Description:
 * @Date: Created in 9:43 2018/7/18
 * @Modified By:
 */
public class DeepCopyByStreamPrototype implements Serializable {

    private ArrayList<String> arrayList = new ArrayList<String>();

    public DeepCopyByStreamPrototype deepCopy() throws IOException, ClassNotFoundException {
        //将对象写入流中
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        //将对象从流中取出来
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (DeepCopyByStreamPrototype) ois.readObject();
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        DeepCopyByStreamPrototype prototype = new DeepCopyByStreamPrototype();
        prototype.arrayList.add("yrs");
        DeepCopyByStreamPrototype copyPrototype = prototype.deepCopy();
        System.out.println(copyPrototype);
    }

}

优点:

* 性能优良
* 逃避构造函数的约束:优点就是减少了约束,缺点也是减少了约束

应用场景:

* 资源优化场景:类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
* 性能和安全要求的场景:通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
* 一个对象多个修改者的场景:

代码地址:https://github.com/ByrsH/Design-Patterns/tree/master/Design%20Patterns/src/main/java/com/yrs/prototype

参考:

《设计模式之禅 第二版》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值