Prototype Pattern —— Creational Class

发布时间 2023-08-30 11:30:40作者: Mysticbinary

Background knowledge

在Java中,Cloneable 接口是一个标记接口(Marker Interface),用于指示实现了该接口的类可以进行克隆操作。它并没有定义任何方法,只是起到一个标记的作用。

要实现克隆功能,需要满足以下两个条件:

  • 类实现了 Cloneable 接口。
  • 在类中重写 clone() 方法。

What is Prototype Pattern

原型模式通过复制现有对象来创建新对象,而无需显式地使用构造函数。原型模式允许动态创建对象,并且可以避免创建子类的复杂性。原型模式可以结合其他设计模式使用,例如工厂方法模式,以便更灵活地创建对象。

这个复制分为两种模式:

  • 深复制
    深克隆(Deep Clone),深克隆是指创建一个新对象,并将原始对象的所有成员变量(无论是值类型还是引用类型)的值都复制到新对象中,包括引用类型成员变量所引用的对象。这样,新对象和原始对象将拥有彼此独立的成员变量副本,彼此之间的修改不会相互影响。深克隆涉及到递归地复制对象及其引用对象的过程,因此可能会比较复杂和耗时。

  • 浅复制
    浅克隆(Shallow Clone),浅克隆是指创建一个新对象,并将原始对象的非引用类型成员变量的值复制到新对象中。对于引用类型成员变量,浅克隆将复制引用,使新对象和原始对象共享相同的引用对象。这意味着在浅克隆后,如果修改其中一个对象的引用类型成员变量,将会影响到另一个对象。因此,浅克隆只复制对象的表面结构,而不涉及引用对象本身的复制。

    浅拷贝的情况下,原被克隆对象发生变化后,克隆对象的基本数据类型不可变引用数据类型(String)的数据不会发生影响,而一些其他字段为可变的应用类型,只要克隆对象的内容随着被克隆对象的变化发生了同样的变化,说明两个对象的属性字段指向同一个引用,才会造成这样的结局。(我就碰到过因为对象被同事插进来的代码导致对象发生了变更,代码出现BUG的问题,后面是使用的深拷贝才消除同事的代码对该对象的影响)。

    如果对象不需要修改属性,不需要做变更,那么用浅拷贝即可。

Key elements

  • 抽象原型类
  • 具体实现类
  • Client

Example of Draw shape —— shallow clone

抽象原型类:

abstract class Shape implements Cloneable {
    protected String type;

    abstract void draw();

    abstract void setType(String type);

    @Override
    public Object clone() {
        Object clone = null;
        try {
            clone = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }
    /*
    在上面的示例中,Shape 类实现了 Cloneable 接口,并重写了 clone() 方法。
    在 Client类 main() 方法中,我们创建了一个原始对象 Circle,然后使用 clone() 方法创建了克隆对象 clone_Circle。

    通过运行示例,我们可以看到原始对象和克隆对象初始时具有相同的值。然后,当我们修改克隆对象的值时,原始对象的值不受影响。
    当然只是针对不可变引用数据类型,其他可变类型会改变。

    需要注意的是,clone() 方法在 Object 类中是受保护的,因此在子类中重写时需要注意访问修饰符。
    此外,对象克隆是浅拷贝的,即对于引用类型的成员变量,只复制引用而不是创建新对象。
    
    如果需要实现深拷贝,需要在 clone() 方法中进行相应的处理。(在clone方法中调用Couple类的clone方法。)

    总结来说,Cloneable 接口是一个标记接口,用于指示类可以进行对象克隆。
    通过实现 Cloneable 接口并重写 clone() 方法,可以在Java中实现对象的浅拷贝。
     */
}

具体实现类:

class Circle extends Shape {

    public Circle() {
        this.type = "Circle";
    }

    public void draw() {
        System.out.println("Drawing a circle.");
    }

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

}

// 
class Rectangle extends Shape {
    public Rectangle() {
        this.type = "Rectangle";
    }

    public void draw() {
        System.out.println("Drawing a rectangle.");
    }

    @Override
    void setType(String type) {

    }
}

Client:

public class Client {
    public static void main(String[] args) {
        Shape rectangle = new Rectangle();
        Shape circle = new Circle();

        Shape clonedRectangle = (Shape) rectangle.clone();
        Shape clonedCircle = (Shape) circle.clone();

        System.out.println("Original Rectangle Type: " + rectangle.type);
        rectangle.draw(); // Drawing a rectangle.
        System.out.println("Cloned Rectangle Type: " + clonedRectangle.type);
        clonedRectangle.draw(); // Drawing a rectangle.

        System.out.println("Original Circle Type: " + circle.type);
        circle.draw(); // Drawing a circle.

        System.out.println("Cloned Circle Type: " + clonedCircle.type);
        clonedCircle.draw(); // Drawing a circle.
        clonedCircle.setType("MyisClonedCircle");
        System.out.println("Cloned Circle Type: " + clonedCircle.type);

        System.out.println("Original Circle Type: " + circle.type);
        circle.draw(); // Drawing a circle.
    }
}