三、原型模式

定义

当创建给定实例的过程很复杂时,就应该使用原型模式(Prototype)。

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

原型模式属于创建型模式。

要点

原型模式允许通过复制现有的实例来创建新的实例,在Java中,这通常意味着使用 clone() 方法或反序列化。

  • 向客户隐藏制造新实例的复杂性。
  • 提供让客户能够产生未知类型对象的选项。
  • 在某些环境下,复制对象比创建对象更有效。

 
在这里插入图片描述

  1. Prototype: 原型类,声明一个克隆自己的接口
  2. ConcretePrototype: 具体的原型类, 实现一个克隆自己的操作
  3. Client: 让一个原型对象克隆自己,从而创建一个新的对象

场景

为了快速构造一个和已有对象相同的副本。 在一个复杂的类层次中,当系统必须从其中的许多类型创建新对象时,可以考虑原型。

例如,克隆羊问题,现在有一只羊实例,请创建和该羊属性完全相同的 3 只羊。

public class Client {

	public static void main(String[] args) {
		// 传统的方法
		Sheep sheep = new Sheep("tom", 1, "白色");

		Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor()); 
		Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor()); 
		Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
	}
	
}

这种传统的方式简单易操作,但是:

  1. 创建新对象时,需要重新获取原始对象的属性,若原始对象相对复杂时,效率较低。
  2. 需要重新初始化对象,而不是动态地获得对象运行时的状态, 不够灵活。

实现

/**
 * 羊,实现了Cloneable接口
 */
public class Sheep implements Cloneable {

    /**
     * 羊的名字
     */
    private String name;
    /**
     * 羊的年龄
     */
    private int age;
    /**
     * 羊的颜色
     */
    private String color;

    /**
     * 该羊的母亲 (clone默认是浅拷贝)
     */
    private Sheep mother;

    public Sheep(String name, int age, String color, Sheep mother) {
        this.name = name;
        this.age = age;
        this.color = color;
        this.mother = mother;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getColor() {
        return color;
    }

    public Sheep getMother() {
        return mother;
    }

    @Override
    public String toString() {
        return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", mother's hashcode = " + mother.hashCode() + "]";
    }

   /**
     * 克隆该实例,使用默认的 clone 方法来完成
     */
    @Override
    protected Object clone() {
        Sheep sheep = null;

        try {
            sheep = (Sheep) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return sheep;
    }

}

Client

/**
 * 使用原型模式创建新对象
 */
public class Client {

    public static void main(String[] args) {
        System.out.println("原型模式完成新对象的创建 --> \n");

        // 原始羊(被克隆的羊)
        Sheep sheep = new Sheep("羊羔", 1, "白色", new Sheep("母亲", 3, "黑白色", null));

        // 克隆羊
        Sheep cloneSheep1 = (Sheep) sheep.clone();
        Sheep cloneSheep2 = (Sheep) sheep.clone();

        // 原始羊信息
        System.out.println("原始羊  : " + sheep);
        // 克隆羊信息
        System.out.println("克隆羊1 : " + cloneSheep1);
        System.out.println("克隆羊2 : " + cloneSheep2);
    }

}

源代码

Click here

总结

  1. 关于原型模式
    创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
    不用重新初始化对象,而是动态地获得对象运行时的状态
    如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
    在实现深克隆的时候可能需要比较复杂的代码

缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了 ocp 原则,这点请注意.

  1. clone 默认使用的是浅拷贝(shadow clone)
    若实例域是基本数据类型,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
    若实例域是引用数据类型,例如某个数组、某个类对象等,那么浅拷贝只会进行引用传递,只是将该成员变量的引用值(内存地址)复制一份给新的对象,实际上使用的是相同的成员变量对象。

应用:Spring 中原型 bean 的创建,就是原型模式的应用

实现深拷贝的方法(deep clone)

  • 使用对象序列化来实现深拷贝(推荐)
  • 重写 clone 方法实现深拷贝

序列化实现

传入的对象需要实现 Serializable 接口

/**
 * 深拷贝一个对象
 */
public Object deepClone(Object src) {
    Object object = null;

    try {
        if (src != null) {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(src);
            oos.close();
            
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            object = ois.readObject();
            ois.close();
        }
    } catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
    }

    return object;
}

重写clone实现

@Override
protected Object clone() {
    Sheep sheep = null;

    try {
        sheep = (Sheep) super.clone();
        sheep.mother = this.mother == null ? null : (Sheep) this.mother.clone();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }

    return sheep;
}
发布了21 篇原创文章 · 获赞 6 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/codedancing/article/details/103323006
今日推荐