Prototype Pattern —— Creational Class
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.
}
}