设计模式之原型模式

设计模式之原型模式

意图

原型模式(Prototype)是一种创建型设计模式, 使你能够复制已有对象, 而又无需使代码依赖它们所属的类。

原型模式主要用于对象的复制,它的核心是就是类图中的原型类 Prototype。Prototype 类需要具备以下两个条件:

  • 实现 Cloneable 接口。在 java 语言有一个 Cloneable 接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用 clone 方法。在 java 虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出 CloneNotSupportedException 异常。
  • 重写 Object 类中的 clone 方法。Java 中,所有类的父类都是 Object 类,Object 类中有一个 clone 方法,作用是返回对象的一个拷贝,但是其作用域 protected 类型的,一般的类无法调用,因此,Prototype 类需要将 clone 方法的作用域修改为 public 类型。

浅拷贝与深拷贝

浅拷贝是指当对象的字段值被复制时,字段引用的对象不会被复制。

例如:如果一个对象有一个指向字符串的字段,并且我们对该对象做了一个浅拷贝,那麽两个对象将引用同一个字符串。

深拷贝是指当一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝。

适用场景

  • 如果你需要复制一些对象, 同时又希望代码独立于这些对象所属的具体类, 可以使用原型模式。
  • 如果子类的区别仅在于其对象的初始化方式, 那么你可以使用该模式来减少子类的数量。 别人创建这些子类的目的可能是为了创建特定类型的对象。

结构

img

  1. 原型 (Prototype) 接口将对克隆方法进行声明。 在绝大多数情况下, 其中只会有一个名为 clone克隆的方法。
  2. 具体原型 (Concrete Prototype) 类将实现克隆方法。 除了将原始对象的数据复制到克隆体中之外, 该方法有时还需处理克隆过程中的极端情况, 例如克隆关联对象和梳理递归依赖等等。
  3. 客户端 (Client) 可以复制实现了原型接口的任何对象。

伪代码

在本例中, 原型模式能让你生成完全相同的几何对象副本, 同时无需代码与对象所属类耦合。

img

所有形状类都遵循同一个提供克隆方法的接口。 在复制自身成员变量值到结果对象前, 子类可调用其父类的克隆方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// 基础原型。
abstract class Shape is
field X: int
field Y: int
field color: string

// 常规构造函数。
constructor Shape() is
// ...

// 原型构造函数。使用已有对象的数值来初始化一个新对象。
constructor Shape(source: Shape) is
this()
this.X = source.X
this.Y = source.Y
this.color = source.color

// clone(克隆)操作会返回一个形状子类。
abstract method clone():Shape


// 具体原型。克隆方法会创建一个新对象并将其传递给构造函数。直到构造函数运
// 行完成前,它都拥有指向新克隆对象的引用。因此,任何人都无法访问未完全生
// 成的克隆对象。这可以保持克隆结果的一致。
class Rectangle extends Shape is
field width: int
field height: int

constructor Rectangle(source: Rectangle) is
// 需要调用父构造函数来复制父类中定义的私有成员变量。
super(source)
this.width = source.width
this.height = source.height

method clone():Shape is
return new Rectangle(this)


class Circle extends Shape is
field radius: int

constructor Circle(source: Circle) is
super(source)
this.radius = source.radius

method clone():Shape is
return new Circle(this)


// 客户端代码中的某个位置。
class Application is
field shapes: array of Shape

constructor Application() is
Circle circle = new Circle()
circle.X = 10
circle.Y = 10
circle.radius = 20
shapes.add(circle)

Circle anotherCircle = circle.clone()
shapes.add(anotherCircle)
// 变量 `anotherCircle(另一个圆)`与 `circle(圆)`对象的内
// 容完全一样。

Rectangle rectangle = new Rectangle()
rectangle.width = 10
rectangle.height = 20
shapes.add(rectangle)

method businessLogic() is
// 原型是很强大的东西,因为它能在不知晓对象类型的情况下生成一个与
// 其完全相同的复制品。
Array shapesCopy = new Array of Shapes.

// 例如,我们不知晓形状数组中元素的具体类型,只知道它们都是形状。
// 但在多态机制的帮助下,当我们在某个形状上调用 `clone(克隆)`
// 方法时,程序会检查其所属的类并调用其中所定义的克隆方法。这样,
// 我们将获得一个正确的复制品,而不是一组简单的形状对象。
foreach (s in shapes) do
shapesCopy.add(s.clone())

// `shapesCopy(形状副本)`数组中包含 `shape(形状)`数组所有
// 子元素的复制品。

案例

使用示例: Java 的 Cloneable (可克隆) 接口就是立即可用的原型模式。

任何类都可通过实现该接口来实现可被克隆的性质。

识别方法: 原型可以简单地通过 clonecopy等方法来识别。

与其他模式的关系

参考资料