设计模式【创建型】-- 原型模式

原型模式(Prototype)

原型模式是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。调用者不需要知道任何创建细节,不调用构造函数

  • 主要应用:
    • 浅拷贝
    • 深拷贝

原型模式:

  • 抽象原型类:规定了具体原型对象必须实现的的 clone() 方法。
  • 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
  • 访问类:使用具体原型类中的 clone() 方法来复制新的对象。
public class Cat implements Cloneable {
    private String name;
    private int total;

    public Cat(String name, int total) {
        this.name = name;
        this.total = total;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getTotal() {
        return total;
    }

    public void setTotal(int total) {
        this.total = total;
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", total=" + total +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Cat c1 = new Cat("短尾猫", 20);
        Cat c2 = (Cat)c1.clone();
        c2.setName("波斯猫");
        System.out.println(" c1---------"+ c1.toString());
        System.out.println(" c2---------"+ c2.toString());
    }
}

设计模式【创建型】-- 原型模式_第1张图片

可以看到,把一个Cat复制过来,只是改了name而已,其他属性完全一样没有改变,需要注意的是,一定要在被拷贝的对象上实现Cloneable接口,否则会抛出CloneNotSupportedException异常

浅拷贝

创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址

public class Animal implements Cloneable {
    private String type;
    private Cat cat;

    public Animal(String type, Cat cat) {
        this.type = type;
        this.cat = cat;
    }

    public Animal() {}

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    
    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    @Override
    public String toString() {
        return "Animal{" +
                "type='" + type + '\'' +
                ", cat=" + cat +
                '}';
    }

    public static void main(String[] args) throws Exception {
        Animal animal = new Animal();
        animal.setType("杂食动物");
        Cat cat = new Cat("小小猫", 12);
        animal.setCat(cat);

        Animal a2 = (Animal) animal.clone();
        Cat c2 = a2.getCat();
        c2.setName("大花猫");

        System.out.println("a1-----" + animal.toString());
        System.out.println("a2-----" + a2.toString());
    }
}

在这里插入图片描述
可以看到,当修改了c2的姓名时,cat 的姓名同样也被修改了,这说明c2和cat 同一个对象,这就是浅克隆的特点,对具体原型类中的引用类型的属性进行引用的复制。同时,这也可能是浅克隆所带来的弊端,因为结合该例子的原意,显然是想在Animal新增一种”大花猫“类型的Cat,而非让所有的Cat都改名叫”大花猫“,于是我们这里就要使用深克隆

深拷贝

创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址

public class Animal implements Cloneable,Serializable{
    private String type;
    private Cat cat;

    public Animal(String type, Cat cat) {
        this.type = type;
        this.cat = cat;
    }

    public Animal() {}

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    protected Object deepClone() 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 ois.readObject();
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    @Override
    public String toString() {
        return "Animal{" +
                "type='" + type + '\'' +
                ", cat=" + cat +
                '}';
    }

    public static void main(String[] args) throws Exception {
        Animal animal = new Animal();
        animal.setType("杂食动物");
        Cat cat = new Cat("小小猫", 12);
        animal.setCat(cat);

        Animal a2 = (Animal)animal.deepClone();
        Cat c2 = a2.getCat();
        c2.setName("大花猫");

        System.out.println("a1-----" + animal.toString());
        System.out.println("a2-----" + a2.toString());
    }
}

在这里插入图片描述

可以看到,当修改了c2name时,cat的name并没有被修改了,这说明c2和cat已经是不同的对象了,说明Animal 中的Cat也被克隆了,不再指向原有对象地址,这就是深克隆。这里需要注意的是,Animal 类和Cat类都需要实现Serializable接口,否则会抛出NotSerializableException异常。

优点

  • 性能优良,Java自带的原型模式是基于内存二进制流的拷贝,比直接new一个对象性能上提升了许多。
  • 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,简化了创建的过程。

缺点

  • 必须配备克隆(或者可拷贝)方法。
  • 当对已有类进行改造的时候,需要修改代码,违反了开闭原则。
  • 深克隆、浅克隆需要运用得当。

适用场景

  • 类初始化消耗资源较多。
  • new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)。
  • 构造函数比较复杂。
  • 循环体中生产大量对象时。

你可能感兴趣的:(设计模式,原型模式)