Loading

原型模式

原型模式

创建对象的方法:构造器,克隆,反射,序列化

什么是原型模式

原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。

优点

  • java自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
  • 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。

缺点

  • 需要为每一个类都配置一个 clone 方法
  • clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
  • 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。

如何实现原型模式

场景:在一个公司中每天都要写日报,记录每天的日程,日报中还有一些建议什么的,大多数情况下员工每天只需要填写今天的日常,建议这一栏大多数情况下都是不用修改的,但我们却每天都需要填写这一栏。

原型模式的实现主要是浅克隆和深克隆

  • 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
  • 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

浅克隆

浅克隆是通过Object类提供的clone()方法来实现的,要克隆的类需要实现Cloneable接口

// 浅克隆
public class ShallowClone {
    public static void main(String[] args) {
        Student stu = new Student();
        Address addr = new Address();
        addr.setAdd("长沙市");
        stu.setNumber(12345);
        stu.setAddress(addr);
        try {
            Student cloneStu = (Student) stu.clone();
            System.out.println("学生1:" + stu.getNumber() + "-" + stu.getAddress().getAdd());
            System.out.println("学生2:" + cloneStu.getNumber() + "-" + stu.getAddress().getAdd());

            stu.setNumber(54321);
            // 原因是浅复制只是复制了addr变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象。
            // 所以,为了达到真正的复制对象,而不是纯粹引用复制。我们需要将Address类可复制化,并且修改clone方法
            addr.setAdd("西湖区");
            System.out.println("学生1:" + stu.getNumber() + "-" + stu.getAddress().getAdd());
            System.out.println("学生2:" + cloneStu.getNumber() + "-" + stu.getAddress().getAdd());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

class Student implements Cloneable {
    private int number;

    private Address address;

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Student stu = null;
        try {
            // 浅拷贝
            stu = (Student) super.clone();
            // 深拷贝
            stu.address = (Address) address.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return stu;
    }
}

class Address implements Cloneable {
    private String add;

    public String getAdd() {
        return add;
    }

    public void setAdd(String add) {
        this.add = add;
    }

    @Override
    public String toString() {
        return "Address{" +
                "add='" + add + '\'' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Address addr = null;
        addr = (Address) super.clone();
        return addr;
    }
}

深克隆

深克隆是通过对象的序列化和反序列化来实现的

public class DeepClone implements Serializable {
    private static final long serializableUID = 369285298572941L;

    // 深克隆,需要对象和对象的所有属性都序列化
    public static <T extends Serializable> T myDeepClone(T obj) throws IOException, ClassNotFoundException {
        // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        // 将流序列化为对象,从内存中取出数据baos.toByteArray()
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        return (T) ois.readObject();
    }
}


class Person implements Serializable {
    private static final long serialVersionUID
            = -9102017020286042305L;

    private String name;    // 姓名
    private int age;        // 年龄
    private Car car;        // 座驾

    public Person(String name, int age, Car car) {
        this.name = name;
        this.age = age;
        this.car = car;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", car=" + car +
                '}';
    }
}

/**
 * 小汽车类
 *
 * @author nnngu
 */
class Car implements Serializable {
    private static final long serialVersionUID
            = -5713945027627603702L;

    private String brand;       // 品牌
    private int maxSpeed;       // 最高时速

    public Car(String brand, int maxSpeed) {
        this.brand = brand;
        this.maxSpeed = maxSpeed;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public int getMaxSpeed() {
        return maxSpeed;
    }

    public void setMaxSpeed(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }

    @Override
    public String toString() {
        return "test.Car{" +
                "brand='" + brand + '\'' +
                ", maxSpeed=" + maxSpeed +
                '}';
    }
}

class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Person p1 = new Person("郭靖", 33,
                new Car("Benz", 300));
        Person p2 = DeepClone.myDeepClone(p1);   // 深度克隆
        // 修改克隆的Person对象p2关联的汽车对象的品牌属性
        // 原来的Person对象p1关联的汽车不会受到任何影响
        // 因为在克隆Person对象时其关联的汽车对象也被克隆了
        p2.getCar().setBrand("BYD");
        System.out.println(p1);

    }
}

原型模式应用场景

  • 对象之间相同或相似,即只是个别的几个属性不同的时候。
  • 创建对象成本较大,例如初始化时间长,占用CPU太多,或者占用网络资源太多等,需要优化资源。
  • 创建一个对象需要繁琐的数据准备或访问权限等,需要提高性能或者提高安全性。
  • 系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值。
posted @ 2021-03-13 15:48  nuoxin  阅读(56)  评论(0)    收藏  举报