--- title: 设计模式之原型模式 date: 2015-06-03 15:00:00 order: 05 categories: - 设计 - 设计模式 tags: - 设计 - 设计模式 permalink: /pages/1204b863/ --- # 设计模式之原型模式 ## 意图 **原型模式**(Prototype)是一种创建型设计模式, 使你能够复制已有对象, 而又无需使代码依赖它们所属的类。 原型模式主要用于对象的复制,它的核心是就是类图中的原型类 Prototype。Prototype 类需要具备以下两个条件: - 实现 Cloneable 接口。在 java 语言有一个 Cloneable 接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用 clone 方法。在 java 虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出 CloneNotSupportedException 异常。 - 重写 Object 类中的 clone 方法。Java 中,所有类的父类都是 Object 类,Object 类中有一个 clone 方法,作用是返回对象的一个拷贝,但是其作用域 protected 类型的,一般的类无法调用,因此,Prototype 类需要将 clone 方法的作用域修改为 public 类型。 ### 浅拷贝与深拷贝 浅拷贝是指当对象的字段值被复制时,字段引用的对象不会被复制。 例如:如果一个对象有一个指向字符串的字段,并且我们对该对象做了一个浅拷贝,那麽两个对象将引用同一个字符串。 深拷贝是指当一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝。 ## 适用场景 - 如果你需要复制一些对象, 同时又希望代码独立于这些对象所属的具体类, 可以使用原型模式。 - 如果子类的区别仅在于其对象的初始化方式, 那么你可以使用该模式来减少子类的数量。 别人创建这些子类的目的可能是为了创建特定类型的对象。 ## 结构 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20210506094301.png) 1. **原型** (Prototype) 接口将对克隆方法进行声明。 在绝大多数情况下, 其中只会有一个名为 `clone`克隆的方法。 2. **具体原型** (Concrete Prototype) 类将实现克隆方法。 除了将原始对象的数据复制到克隆体中之外, 该方法有时还需处理克隆过程中的极端情况, 例如克隆关联对象和梳理递归依赖等等。 3. **客户端** (Client) 可以复制实现了原型接口的任何对象。 ## 伪代码 在本例中, **原型**模式能让你生成完全相同的几何对象副本, 同时无需代码与对象所属类耦合。 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20210506095002.png) 所有形状类都遵循同一个提供克隆方法的接口。 在复制自身成员变量值到结果对象前, 子类可调用其父类的克隆方法。 ```java // 基础原型。 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` (可克隆) 接口就是立即可用的原型模式。 任何类都可通过实现该接口来实现可被克隆的性质。 - [`java.lang.Object#clone()`](http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#clone--) (类必须实现 [`java.lang.Cloneable`](http://docs.oracle.com/javase/8/docs/api/java/lang/Cloneable.html) 接口) **识别方法**: 原型可以简单地通过 `clone`或 `copy`等方法来识别。 ## 与其他模式的关系 - 在许多设计工作的初期都会使用[工厂方法模式](https://refactoringguru.cn/design-patterns/factory-method) (较为简单, 而且可以更方便地通过子类进行定制), 随后演化为使用[抽象工厂模式](https://refactoringguru.cn/design-patterns/abstract-factory)、 [原型模式](https://refactoringguru.cn/design-patterns/prototype)或[生成器模式](https://refactoringguru.cn/design-patterns/builder) (更灵活但更加复杂)。 - [抽象工厂模式](https://refactoringguru.cn/design-patterns/abstract-factory)通常基于一组[工厂方法](https://refactoringguru.cn/design-patterns/factory-method), 但你也可以使用[原型模式](https://refactoringguru.cn/design-patterns/prototype)来生成这些类的方法。 - [原型](https://refactoringguru.cn/design-patterns/prototype)可用于保存[命令模式](https://refactoringguru.cn/design-patterns/command)的历史记录。 - 大量使用[组合模式](https://refactoringguru.cn/design-patterns/composite)和[装饰模式](https://refactoringguru.cn/design-patterns/decorator)的设计通常可从对于[原型](https://refactoringguru.cn/design-patterns/prototype)的使用中获益。 你可以通过该模式来复制复杂结构, 而非从零开始重新构造。 - [原型](https://refactoringguru.cn/design-patterns/prototype)并不基于继承, 因此没有继承的缺点。 另一方面, *原型*需要对被复制对象进行复杂的初始化。 [工厂方法](https://refactoringguru.cn/design-patterns/factory-method)基于继承, 但是它不需要初始化步骤。 - 有时候[原型](https://refactoringguru.cn/design-patterns/prototype)可以作为[备忘录模式](https://refactoringguru.cn/design-patterns/memento)的一个简化版本, 其条件是你需要在历史记录中存储的对象的状态比较简单, 不需要链接其他外部资源, 或者链接可以方便地重建。 - [抽象工厂](https://refactoringguru.cn/design-patterns/abstract-factory)、 [生成器](https://refactoringguru.cn/design-patterns/builder)和[原型](https://refactoringguru.cn/design-patterns/prototype)都可以用[单例模式](https://refactoringguru.cn/design-patterns/singleton)来实现。 ## 参考资料 - [《Head First 设计模式》](https://book.douban.com/subject/2243615/) - [《大话设计模式》](https://book.douban.com/subject/2334288/) - [设计模式教程](https://refactoringguru.cn/design-patterns/catalog)