Background knowledge

在Java中,Cloneable 接口是一个标记接口(Marker Interface,它们内部都没有方法和属性),实现Cloneable接口表示该对象能被克隆,能使用Object.clone()方法。

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

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

What is Prototype Pattern

为了方便记忆,原型模式也可以叫克隆模式

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

这个复制分为两种模式:

  • 深复制
    深克隆(Deep Clone),深克隆是指创建一个新对象,并将原始对象的所有成员变量(无论是值类型还是引用类型)的值都复制到新对象中,包括引用类型成员变量所引用的对象。这样,新对象和原始对象将拥有彼此独立的成员变量副本,彼此之间的修改不会相互影响。深克隆涉及到递归地复制对象及其引用对象的过程,因此可能会比较复杂和耗时。
    一般考虑使用深克隆,就是因为有可变的引用类型会受影响, 既然引用类型无法被完全克隆,那么我们可以考虑在引用类型所在的类也实现Cloneable接口。有多少个引用类型,就去实现多少个Cloneable接口。

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

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

    如果克隆的对象内没有可变的引用对象,都是只用使用一些基础类型,那么直接用浅拷贝即可。

原型模式可以实现深克隆或浅克隆,具体实现取决于需求。

配合其他设计模式:
原型模式可以结合其他设计模式使用,例如工厂方法模式,以便更灵活地创建对象。

Key elements

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

Example of Draw shape —— shallow clone

以下是一个简单的浅克隆例子。

抽象原型类:

abstract class Shape implements Cloneable {
    protected String type;

    abstract void draw();

    @Override
    public Object clone() {
        Object clone = null;
        try {
            clone = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }
}

具体实现类:

// 具体原型类 - Rectangle
class Rectangle extends Shape {
    public Rectangle() {
        this.type = "Rectangle";
    }

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

// 具体原型类 - Circle
class Circle extends Shape {
    public Circle() {
        this.type = "Circle";
    }

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

Client:

// 客户端代码
public class Main {
    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.
    }
}

在上面的示例中,我们定义了抽象原型类 Shape,它包含了 clone() 方法和 draw() 方法。然后,我们实现了具体的原型类 Rectangle 和 Circle,它们继承自 Shape 并实现了相应的方法。

在客户端代码中,我们首先创建了一个原始的 Rectangle 对象和一个原始的 Circle 对象。然后,我们分别对它们进行克隆,并得到克隆后的 Shape 对象。

通过原型模式,我们可以通过克隆(浅克隆)现有对象来创建新对象,而无需显式地调用构造函数。这消除了重复创建相似对象的需要,并提高了性能。

Reference

深克隆请参考这个文章
https://cloud.tencent.com/developer/article/1628044

posted on 2023-08-30 11:22  Mysticbinary  阅读(42)  评论(0编辑  收藏  举报