原型模式
又叫做克隆,Prototype模式
设计目的
原型模式是一种创建型设计模式,使你能够复制已有的对象,而无需依赖他们所属的类。
应用
如果你有一个对象,并且希望生成一个和这个对象一毛一样的复制品,那该如何实现呢?
首先你必须新建一个同类的对象,然后,遍历原始对象的所有成员变量,将这些成员变量复制到新对象中。
但是有个小问题,并非所有的对象都能以这种方式复制,因为有些对象中的私有成员变量对外是不可见的。
还有一个问题,因为你必须知道对象所属的类才能创建复制品,所以代码必须依赖该类。即使你可以接受这种依赖性,但是有时候你只知道对象的某个方法的某个参数实现了某个接口的任一对象,你不知道他所属的具体类,那该怎么办?
解决方法
原型模式将克隆过程委派给被克隆的对象。模式为所有支持克隆的对象声明了一个通用的接口,这个接口能够让你克隆对象,同时无需将代码和对象所属的类耦合。通常情况下,这样的接口中仅仅包含一个克隆方法。
该方法会创建一个当前类的对象,然后将原始对象所有的成员变量值复制得到新建的类中。你甚至可以复制私有的成员变量。
支持克隆的对象就是原型。当你的对象有几十个甚至几百中成员变量时,对其克隆甚至可以代替子类的构造。。
例如:细胞的有丝分裂,有丝分裂会产生一对完全相同的细胞。原始细胞就是一个原型,它在复制体的生成过程中起到了推动作用。
原型模式代码
Shape抽象类
public abstract class Shape {
public int x;
public int y;
public String color;
public Shape(Shape shape){
if (shape!=null){
this.x = shape.x;
this.y = shape.y;
this.color = shape.color;
}
}
public Shape() {
}
public abstract Shape clone();
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Shape)) return false;
Shape shape = (Shape) o;
return x == shape.x &&
y == shape.y &&
Objects.equals(color, shape.color);
}
@Override
public int hashCode() {
return Objects.hash(x, y, color);
}
}
Circle类
public class Circle extends Shape{
public int radius;
public Circle(){}
public Circle(Circle target){
super(target);
if (target!=null){
this.radius = target.radius;
}
}
@Override
public Shape clone() {
return new Circle(this);
}
@Override
public boolean equals(Object object2) {
if (!(object2 instanceof Circle) || !super.equals(object2)) return false;
Circle shape2 = (Circle) object2;
return shape2.radius == radius;
}
@Override
public int hashCode() {
return Objects.hashCode(super.hashCode(), radius);
}
}
Rectangle类
public class Rectangle extends Shape {
public int width;
public int height;
public Rectangle(Rectangle target) {
super(target);
if (target!=null){
this.height = target.height;
this.width = target.width;
}
}
public Rectangle() {
}
@Override
public Shape clone() {
return new Rectangle(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Rectangle)) return false;
if (!super.equals(o)) return false;
Rectangle rectangle = (Rectangle) o;
return width == rectangle.width &&
height == rectangle.height;
}
@Override
public int hashCode() {
return Objects.hashCode(super.hashCode(), width, height);
}
}
测试
Circle circle = new Circle();
circle.x = 10;
circle.y = 20;
circle.radius = 15;
circle.color = "red";
Circle anotherCircle = (Circle) circle.clone();
System.out.println(circle.equals(anotherCircle));//true
Rectangle rectangle = new Rectangle();
rectangle.width = 10;
rectangle.height = 20;
rectangle.color = "blue";
Rectangle rectangleCopy = (Rectangle) rectangle.clone();
System.out.println(rectangle.equals(rectangleCopy));//true
适用场景
- 如果你需要复制对象,同时有希望代码独立于这些对象所属的具体类,可以使用原型模式。
- 如果子类的区别仅仅在于对象的初始化方式,那么可以使用原型模式减少子类的数量。
实现方式
- 创建原型接口,并在其中声明克隆方法。
- 原型类必须定义一个以该类对象为参数的构造函数。构造函数必须复制参数对象中的所有成员变量值到新建实体中。如果那你需要修改子类,必须调用父类构造函数,让附列复制私有成员变量的值。
- 克隆方法通常只有一行代码:使用new运算符调用原型版本的构造函数。注意,每个类都必须显示重写克隆方法并使用自身类名调用new运算符。否则,克隆方法可能会生成父类对象。
优缺点
优点:
- 克隆对象与他们所属的类解耦。
- 克隆预生成原型,避免反复运行初始化代码。
- 可以更方便生成复杂对象。
- 可以用继承以外的方式处理复杂对象的不同配置
缺点: - 被克隆的对象如果包含循环引用可能会很麻烦。
推荐一个学习设计模式的在线网站
https://refactoringguru.cn/design-patterns