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

大家可以想象一下工厂生产餐盘的场景,给盘印上花纹,如果一个一个的手工印上去,太费时费力了。那么在代码里面,有没有一种方法可以解决这类的问题呢。本篇文件和大家介绍下原型模式,它是怎么解决这种重复工作的。

接下来我们就来聊聊原型模式
定义
通过复制现有实例来创建新的实例,无需知道相应类的信息

分类

  • 深拷贝:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。
  • 浅拷贝:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。

实现方式

  • Java
    Object是所有类的根类,Object类提供了一个clone()方法,该方法可以将一个 Java 对象复制一份,但是需要实现 clone的Java类必须要实现一个接口Cloneable,该接口表示该类能够复制且具有复制的能力。

  • 前端
    通过Object.create(),会使用现有的对象来提供新创建的对象的__proto__

一、浅克隆

示例1:

@Data
public class ShallowClone implements Cloneable {
    private String name;
    private Date birthday;
    private Integer age;
    private char sex;

    @Override
    public ShallowClone clone() throws 
CloneNotSupportedException {
        return (ShallowClone) super.clone();
    }

    @Override
    public String toString() {
        return JSON.toJSONString(this);
    }
}
public static void main(String[] args) throws CloneNotSupportedException {
        ShallowClone shallowClone = new ShallowClone();
        shallowClone.setAge(18);
        shallowClone.setSex('男');
        shallowClone.setName("张三");
        shallowClone.setBirthday(new Date());
        System.out.println(shallowClone.toString());
        //{"age":18,"birthday":1678950596888,"name":"张三","sex":"男"}

        ShallowClone clone1 = shallowClone.clone();
        System.out.println(clone1.toString());
        //{"age":18,"birthday":1678950596888,"name":"张三","sex":"男"}
}

接下来,我们改变String类型和Date类型的值

/**
  * 改变之前
  */
public static void main(String[] args) throws CloneNotSupportedException {
        ShallowClone clone1 = new ShallowClone(18,18, “张三”, new Date());
        ShallowClone clone2 = clone1.clone();

        System.out.println(clone1.getBirthday().hashCode());
        // -379106616
        System.out.println(clone2.getBirthday().hashCode());
        // -379106616
        System.out.println(clone1.getName().hashCode());
        // 774889
        System.out.println(clone2.getName().hashCode());
        // 774889
    }

改变之后:
clone1.setBirthday(format("2022-12-12"));
System.out.println(clone1.getBirthday().hashCode());
// 32122245 
System.out.println(clone2.getBirthday().hashCode());
// -378671717
clone1.setName("李四");
System.out.println(clone1.getName().hashCode());
// 842061
System.out.println(clone2.getName().hashCode());
// 774889

示例2: 我们新增一个DeepClone对象
@Data
public class DeepClone implements Cloneable {
    private String stature;
    private Double weight;

    @Override
    public DeepClone clone() throws CloneNotSupportedException {
        return (DeepClone) super.clone();
    }
    @Override
    public String toString() {
        return JSON.toJSONString(this);
    }
}

/**
  * 在ShallowClone中引入DeepClone对象
  */
public class ShallowClone implements Cloneable {
    private String name;
    private Date birthday;
    private Integer age;
    private char sex;
    private DeepClone deep;
   ...
}

public static void main(String[] args) throws CloneNotSupportedException {
        ShallowClone clone1 = new ShallowClone((18,18, “张三”, new Date()));
        DeepClone deepClone1 = new DeepClone(56.3, "174CM");
        clone1.setDeep(deepClone1);
        ShallowClone clone2 = clone1.clone();

        DeepClone deep = clone1.getDeep();
        deep.setStature("184CM");
        System.out.println(clone1.getDeep().hashCode());//-829875887
        System.out.println(clone2.getDeep().hashCode());//-829875887
        System.out.println(clone1.toString());
//{"age":18,"birthday":1678955695170,"deep":{"stature":"184CM","weight":56.3},"name":"张三","sex":"男"}
        System.out.println(clone2.toString());
//{"age":18,"birthday":1678955695170,"deep":{"stature":"184CM","weight":56.3},"name":"张三","sex":"男"}
    }

结论

  • 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递。
  • 对于数据类型是封装类型(如:String、Integer、Date等)或者是引用数据类型的成员变量(如:数组、某个类等),那么浅拷贝会进行引用传递。但封装类型在重新赋值时会重新分配空间,不会对原数据产生变化。

二、深克隆

无论是成员变量石值类型还是引用类型,都将复制一份给克隆对象
现在我们重写下ShallowClone的clone()方法

@Override
public ShallowClone clone() throws CloneNotSupportedException {
    ShallowClone clone = (ShallowClone) super.clone();
    clone.deep = deep.clone();
    return clone;
}

/**
  * 再次运行
  */
public static void main(String[] args) throws CloneNotSupportedException {
        ShallowClone clone1 = new ShallowClone((18,18, “张三”, new Date()));
        DeepClone deepClone1 = new DeepClone(56.3, "174CM");
        clone1.setDeep(deepClone1);
        ShallowClone clone2 = clone1.clone();

        DeepClone deep = clone1.getDeep();
        deep.setStature("184CM");
        System.out.println(clone1.getDeep().hashCode());//-829875887
        System.out.println(clone2.getDeep().hashCode());//-829875887
        System.out.println(clone1.toString());
//{"age":18,"birthday":1678955695170,"deep":{"stature":"184CM","weight":56.3},"name":"张三","sex":"男"}
        System.out.println(clone2.toString());
//{"age":18,"birthday":1678956832142,"deep":{"stature":"174CM","weight":56.3},"name":"张三","sex":"男"}
}

我们再来看个示例
/**
  * 不使用Object自带的clone(),新增deepCloe()方法,使用流来实现深克隆
  */
public ShallowClone deepCloe() throws IOException, ClassNotFoundException {
        // 将对象写入到流中
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(this);
        // 将对象从流中取出
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        return (ShallowClone) ois.readObject();
}

结论

  • 深克隆技术实现了原型对象和克隆对象的完全独立,对任意克隆对象的修改都不会给其他对象产生影响,是一种更为理想的克隆实现方式。

优缺点:

优点:

  1. 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率。
  2. 原型模式提供了简化的创建结构,工厂方法模式常常需要有一个与产品类等级结构相同的工厂等级结构,而原型模式就不需要这样,原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品。
  3. 可以使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(如恢复到某一历史状态),可辅助实现撤销操作。

缺点:

  1. 需要为每一个类配备一个克隆方法
  2. 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦。

适用场景

  1. 创建新对象成本较大,新的对象可以通过原型模式对已有对象进行复制来获得,如果是相似对象,则可以对其成员变量稍作修改。

  2. 如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占用内存较少时,可以使用原型模式配合备忘录模式来实现

  3. 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。

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