原型模式

  • 解决问题
    • 对象创建成本比较大,比如需要 DB、权限等外部资源。
    • 复杂依赖,比如嵌套依赖等
    • 保护性拷贝、记录历史操作场景
  • 方法
    • 浅拷贝
    • 深拷贝
    • 还可以类似于 Redis 渐进式 rehash,copy-on-write,需要更改时再处理

1 原型模式

1.1 解决的问题

如果对象的 创建成本较大,且同一个类的不同对象间差别不大(大部分字段相同)的情况下,可以利用已有对象(原型)进行复制(或者叫拷贝、克隆)的方式来创建新的对象,以达到节省创建时间的目的。

简单说,就是实现克隆 clone

1.2 模式结构

1.3 具体实现

即深拷贝实现。

  1. 引用类型实现 Cloneable 接口,重写 clone 方法。
public class Assets implements Cloneable{
    private int amount;     // 数目
    private Money money;    // 币种
    private String kind;    // 资金种类
 
	@Override
	protected Object clone() {
		Assets assets = null;
		try {
			assets = (Assets)super.clone();
			assets.setMoney((Money)this.getMoney().clone());
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return assets;
	}
}

注:深拷贝需要整个克隆涉及的对象都正确的实现 clone()方法,其只能怪一个没正确实现克隆,都会导致深拷贝失败。

  1. 序列化,定义一个方法完成对象转二进制流和二进制流转对象,然后返回反序列化后的对象。
public Assets deepClone() {
    try {
        // 写入当前对象的二进制流
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        // 读取二进制流产生新对象
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (Assets)ois.readObject();
    } catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
    }
    return null;
}
 

1.4 应用场景

简单点说就是以某个对象为原型,克隆出一个一模一样的对象,常见应用场景:

  • 资源优化 (对象初始化需使用较多外部资源时,如 IO、数据库、CPU,权限等);
  • 复杂依赖 (依赖嵌套,A 创建依赖 B,B 创建依赖 C,一连串的对象 get 和 set);
  • 同一对象多个修改者 (多个对象的调用者都需要修改对象的值,克隆多个供使用其使用,保护性拷贝);
  • 需保存原始对象状态 (如记录历史操作的场景,通过原型模式快速保存记录);
  • 结合工厂模式使用 (定义统一的复制接口,如 clone、copy,使用一个工厂来统一进行拷贝和新对象创建,然后由工厂方法提供给调用者使用)。