Preview
Skip to content
CodingDiary
返回

设计模式之创建型设计模式-原型模式

编辑页面
导读

clone() 方法你真的会用吗?浅克隆的坑你踩过没?引用对象地址相同,改一个全变了!本文用「印钞机打印纸币」的场景,对比浅克隆 vs 深克隆的实现差异。深克隆需要实现 Serializable 接口,通过序列化/反序列化实现真正的对象复制。附 BeanUtils、JSON.parseObject 等常见应用。

1. 模式简介

原型实例指定创建对象的种类,通过克隆这些原型创建新的对象。调用者不需要指定对象的创建细节,不通过调用构造函数创建对象。属于创建型设计模式

2. 示例代码

2.1. 浅克隆

2.1.1. 代码实现

/**
 * <p>
 * 钱
 * </p>
 *
 * @author yangkai.shen
 * @date Created in 2019-08-14 15:53
 */
public interface Money {
    /**
     * 打印
     *
     * @return {@link Money}
     */
    Money print();
}
/**
 * <p>
 * 形状
 * </p>
 *
 * @author yangkai.shen
 * @date Created in 2019-08-14 15:59
 */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Shape implements Serializable {
    /**
     * 描述
     */
    private String desc;
}

/**
 * <p>
 * 一百元
 * </p>
 *
 * @author yangkai.shen
 * @date Created in 2019-08-14 15:56
 */
@Getter
@Setter
public class HundredMoney implements Money {
    private Shape shape;

    /**
     * 打印
     *
     * @return {@link Money}
     */
    @Override
    public Money print() {
        HundredMoney hundredMoney = new HundredMoney();
        hundredMoney.setShape(this.shape);
        return hundredMoney;
    }
}
/**
 * <p>
 * 原型模式,浅克隆测试
 * </p>
 *
 * @author yangkai.shen
 * @date Created in 2019-08-14 16:35
 */
public class PatternTest {
    public static void main(String[] args) {
        HundredMoney money1 = new HundredMoney();
        money1.setShape(new Shape("纸币"));

        // 原型模式 浅克隆
        HundredMoney money2 = (HundredMoney) money1.print();

        System.out.println("money1 -> 引用对象地址:" + money1.getShape());
        System.out.println("money2 -> 引用对象地址:" + money2.getShape());
        System.out.println("引用对象地址比较:" + (money1.getShape() == money2.getShape()));
    }
}
money1 -> 引用对象地址:com.xkcoding.design.pattern.creational.prototype.Shape@1d44bcfa
money2 -> 引用对象地址:com.xkcoding.design.pattern.creational.prototype.Shape@1d44bcfa
引用对象地址比较:true

2.1.2. UML图例

原型模式-浅克隆UML图例

2.2. 深克隆

深克隆一定需要实现 Serializable 接口

2.2.1. 代码实现

/**
 * <p>
 * 钱
 * </p>
 *
 * @author yangkai.shen
 * @date Created in 2019-08-14 15:53
 */
public interface Money {
    /**
     * 打印
     *
     * @return {@link Money}
     */
    Money print();
}
/**
 * <p>
 * 形状
 * </p>
 *
 * @author yangkai.shen
 * @date Created in 2019-08-14 15:59
 */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Shape implements Serializable {
    /**
     * 描述
     */
    private String desc;
}

/**
 * <p>
 * 一百元
 * </p>
 *
 * @author yangkai.shen
 * @date Created in 2019-08-14 15:56
 */
@Getter
@Setter
public class HundredMoney implements Money, Cloneable, Serializable {
    private Shape shape;

    /**
     * 打印
     *
     * @return {@link Money}
     */
    @Override
    public Money print() {
        return (Money) this.clone();
    }

    @Override
    protected Object clone() {
        return this.deepClone();
    }

    @SneakyThrows
    private Object deepClone() {
        @Cleanup ByteArrayOutputStream bos = new ByteArrayOutputStream();
        @Cleanup ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        @Cleanup ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        @Cleanup ObjectInputStream ois = new ObjectInputStream(bis);

        return ois.readObject();
    }

}
/**
 * <p>
 * 原型模式,深克隆测试,注意所有引用对象均需要实现 {@link java.io.Serializable} 接口
 * </p>
 *
 * @author yangkai.shen
 * @date Created in 2019-08-14 17:29
 */
public class PatternTest {
    public static void main(String[] args) {
        HundredMoney money1 = new HundredMoney();
        money1.setShape(new Shape("纸币"));

        // 原型模式 深克隆
        HundredMoney money2 = (HundredMoney) money1.print();

        System.out.println("money1 -> 引用对象地址:" + money1.getShape());
        System.out.println("money2 -> 引用对象地址:" + money2.getShape());
        System.out.println("引用对象地址比较:" + (money1.getShape() == money2.getShape()));
    }
}
money1 -> 引用对象地址:com.xkcoding.design.pattern.creational.prototype.Shape@355da254
money2 -> 引用对象地址:com.xkcoding.design.pattern.creational.prototype.Shape@12edcd21
引用对象地址比较:false

2.2.2. UML图例

原型模式-深克隆UML图例

3. 应用

// BeanUtils.copyProperties()

// JSON.parseObject()

// Guava copy 的工具类

// spring 中的 scope = "prototype" 就是通过加载 Spring 容器中的对象模板,复制出多实例的

// JDK 中 Arrays.copyOf()

4. 场景

6. 优缺点

优点: 原型模式性能比直接new一个对象性能高;简化了创建过程

缺点: 必须配备克隆(或者可拷贝)方法;对克隆复杂对象或者对克隆出的对象进行复杂改造时,容易带来风险;浅克隆深克隆 要运用得当

7. 完整代码地址

https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/creational/prototype


编辑页面
分享到:

上一篇
设计模式之结构型设计模式-代理模式
下一篇
设计模式之创建型设计模式-单例模式