原型模式(Prototype)
Prototype:原型模式是一种创建型设计模式,使你能够复制原有的对象,而又无需使代码依赖它所需的类;用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
/**
* 原型模式
*
* @author CAI
* @time 2020/11/7
*/
public class PrototypePattern {
public static void main(String[] args) {
List<Shape> shapes = new ArrayList<>();
List<Shape> shapesCopy = new ArrayList<>();
Circle circle = new Circle();
circle.x = 10;
circle.y = 20;
circle.radius = 15;
circle.color = "red";
shapes.add(circle);
Circle anotherCircle = (Circle) circle.clone();
shapes.add(anotherCircle);
Rectangle rectangle = new Rectangle();
rectangle.width = 10;
rectangle.height = 20;
rectangle.color = "blue";
shapes.add(rectangle);
cloneAndCompare(shapes, shapesCopy);
}
/**
* 克隆和比较
*
* @param shapes 形状列表
* @param shapesCopy 克隆形状列表
*/
private static void cloneAndCompare(List<Shape> shapes, List<Shape> shapesCopy) {
// 克隆
for (Shape shape : shapes) {
shapesCopy.add(shape.clone());
}
for (int i = 0; i < shapes.size(); i++) {
// 内存地址是否相同
if (shapes.get(i) != shapesCopy.get(i)) {
System.out.println(i + " : 与克隆对象不是同一个对象");
// 对象属性是否相同
if (shapes.get(i).equals(shapesCopy.get(i))) {
System.out.println(i + ": 与克隆对象属性相同");
} else {
System.out.println(i + ": But they are not identical (booo!)");
}
} else {
System.out.println(i + ": 与克隆对象属性不同");
}
}
}
}
/**
* 通用形状:原型接口或抽象类
*/
abstract class Shape {
public int x;
public int y;
public String color;
public Shape() { }
public Shape(Shape target) {
if (target != null) {
this.x = target.x;
this.y = target.y;
this.color = target.color;
}
}
protected abstract Shape clone();
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Shape)) {
return false;
}
Shape shape = (Shape) obj;
return shape.x == x && shape.y == y && Objects.equals(shape.color, color);
}
}
/**
* 简单形状:具体原型
*/
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 obj) {
if (!(obj instanceof Circle) || !super.equals(obj)) {
return false;
}
Circle shape = (Circle) obj;
return shape.radius == radius;
}
}
/**
* 另一个形状:具体原型
*/
class Rectangle extends Shape {
public int width;
public int height;
public Rectangle() { }
public Rectangle(Rectangle target) {
super(target);
if (target != null) {
this.width = target.width;
this.height = target.height;
}
}
@Override
public Shape clone() {
return new Rectangle(this);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Rectangle) || !super.equals(obj)) {
return false;
}
Rectangle shape = (Rectangle) obj;
return shape.width == width && shape.height == height;
}
}
- 形状:原型的接口或抽象类,声明
clone();在绝大多数情况下,只会有一个名为clone的方法。 - 简单形状/其他形状:原型的具体实现类,实现
clone方法。除了将原始对象的数据复制到克隆体中,该方法还需要处理克隆过程中的极端情况。如:对象中涉及引用对象或递归依赖等。
原型注册表
/**
* 原型注册表
*
* @author Viices Cai
* @time 2022/1/13
*/
public class PrototypeRegedit {
public static void main(String[] args) {
BundledShapeCache cache = new BundledShapeCache();
Shape shape1 = cache.get("Big green circle");
Shape shape2 = cache.get("Medium blue rectangle");
Shape shape3 = cache.get("Medium blue rectangle");
if (shape1 != shape2 && !shape1.equals(shape2)) {
System.out.println("Big green circle 与 Medium blue rectangle 不是同一个对象.");
} else {
System.out.println("Big green circle 与 Medium blue rectangle 是同一个对象.");
}
if (shape2 != shape3) {
System.out.println("两个 Medium blue rectangle 不是同一个对象.");
if (shape2.equals(shape3)) {
System.out.println("两个 Medium blue rectangle 属性相同.");
} else {
System.out.println("两个 Medium blue rectangle 属性不相同.");
}
} else {
System.out.println("两个 Medium blue rectangle 是同一个对象.");
}
}
}
/**
* 原型工厂
*/
class BundledShapeCache {
private Map<String, Shape> cache = new HashMap<>();
public BundledShapeCache() {
Circle circle = new Circle();
circle.x = 5;
circle.y = 7;
circle.radius = 45;
circle.color = "Green";
Rectangle rectangle = new Rectangle();
rectangle.x = 6;
rectangle.y = 9;
rectangle.width = 8;
rectangle.height = 10;
rectangle.color = "Blue";
cache.put("Big green circle", circle);
cache.put("Medium blue rectangle", rectangle);
}
public Shape put(String key, Shape shape) {
cache.put(key, shape);
return shape;
}
public Shape get(String key) {
return cache.get(key).clone();
}
}
-
原型注册表:提供一种访问常用原型的简单方法,其中存储了一系列可供随时复制的预生成对象。
- 最简易的实现是维护一个
name -> prototype的map,如需要使用名称之外的条件进行搜索,可以在此基础上进行改造。
- 最简易的实现是维护一个
使用场景
-
经常面临某些结构复杂的对象的创建工作,这些对象经常面临着剧烈的变化,但是拥有比较稳定一致的接口可以使用原型模式。
-
如果需要复制一些对象,同时又希望代码独立于这些对象所属的具体类,可以使用原型模式。
- 通常出现于在需要处理对第三方的接口代码传递过来的对象时;原型模式为调用者提供一个通用的接口,调用者可以通过这个接口与所有实现了克隆的对象进行交互,也使得调用的代码和其克隆的具体对象类独立开。
-
如果子类的区别仅在于其对象的初始化方式,可以使用该模式来减少子类的数量。
- 在原型模式中可以使用一系列预生成的、各种类型的对象作为原型。调用者不必根据需求对子类进行初始化,只需要调用合适的原型并进行克隆。
优缺点
-
优点:
- 每
NEW一次,都需要执行一次构造函数,多次执行该操作过于低效。一般在初始化的信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,又提高了性能。 - 不用重新初始化对象,而是动态地获得对象运行时的状态。
- 克隆对象时,无需与所属的具体类相耦合。
- 更方便地生成复杂对象。
- 可以用继承之外的方式来处理复杂对象的不同配置。
- 每
-
缺点:
- 克隆包含循环引用的复杂对象可能会非常麻烦。
在 JAVA 中的运用
-
Java中的Cloneable接口就是立即可以的原型模式。- 任何类都可以通过该接口来实现可被克隆的性质。
- 原型可以简单的通过
clone()和copy()进行辨别。
引出概念
- 浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。
- 深拷贝:把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。