概念

原型模式(Prototype Pattern)是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而无需通过标准的构造函数来创建

原型模式通过引入一个原型接口和具体原型实现类来实现这一点


在原型模式中,通常存在两个核心角色:原型接口(Prototype)和具体原型类(Concrete Prototype)

原型接口定义了用于复制自身的方法,而具体原型类实现了原型接口,负责实际进行对象的复制操作


原型模式的核心思想是通过复制现有对象来创建新对象,而无需知道具体对象的类型或者构造方法

这种模式的优点在于,可以避免对象的构造过程,提高对象的创建效率,同时也可以减少代码重复

原型模式适用于以下情况:

  1. 当一个系统需要创建大量相似对象时,使用原型模式可以提高对象创建的效率
  2. 当对象的构造过程比较复杂,或者需要从数据库或者网络中获取对象的数据时,使用原型模式可以避免这些复杂的构造过程


举个简单的例子,考虑一个图形编辑器

用户可以创建不同类型的图形对象(如圆形、矩形等),而每种图形对象可能需要不同的初始化参数(如半径、宽度、高度等)

使用原型模式,可以在图形对象创建时复制已有的图形对象,然后根据需要修改其初始化参数,从而快速创建新的图形对象,避免了重新构造每种类型的图形对象


实现条件

  1. 当一个系统应该独立于它的产品创建、构成和表示时
  2. 当要实例化的类是在运行时指定时,例如,通过动态加载
  3. 为了避免创建一个与产品类层次平行的工厂类层次
  4. 当一个类的实例只有几个不同状态组合中的一种时。安装相应数量的原型并克隆它们可能比每次用适当的状态手动实例化该类更方便


优点

  • 避免子类化

    通过复制一个原型而不是请求一个工厂方法来创建一个对象,可以不用创建一个与产品类层次平行的工厂类层次

  • 简化对象的创建

    特别是当对象的创建过程比复制一个现有的实例更复杂或更昂贵时

  • 优化性能

    在实例化操作成本较高时,原型模式可以显著提高性能,因为clone通常比创建新实例更加高效


缺点

  • 复制复杂对象困难

    如果对象之间存在循环引用,或者对象的结构非常复杂,那么复制(克隆)可能很困难


实现方式

在ES6+中,最佳实践是使用class关键字来定义原型,并通过Object.create或者类的构造函数来创建新的实例

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
/*
* @Description: 创建型-原型模式
* @Author: 5t5
* @Time: 2024/4/9 17:45
*/

class GenericPrototypePattern {
constructor(properties) {
for (let prop in properties) {
if (properties.hasOwnProperty(prop)) {
this[prop] = properties[prop];
}
}
}

printProperties() {
for (let prop in this) {
if (this.hasOwnProperty(prop)) {
console.log(`${prop}: ${this[prop]}`);
}
}
}

clone() {
// 创建一个新的对象,将当前对象的属性复制到新对象上
let clonedObject = new GenericPrototypePattern({});
for (let prop in this) {
if (this.hasOwnProperty(prop)) {
clonedObject[prop] = this[prop];
}
}
return clonedObject;
}
}

export default GenericPrototypePattern

// region HOW TO USE
// 使用原型模式创建新对象
const obj1 = new GenericPrototypePattern({name: 'Object 1', value: 10});
const obj2 = obj1.clone();

// 修改克隆后的对象的属性
obj2.name = 'Object 2';
obj2.color = 'blue';

// 调用通用方法
obj1.printProperties(); // 输出: name: Object 1, value: 10
obj2.printProperties(); // 输出: name: Object 2, value: 10, color: blue
// endregion HOW TO USE

这段代码定义了一个名为 GenericPrototypePattern 的类,它实现了原型模式。这个类包含了构造函数、一个打印属性的方法 printProperties() 和一个克隆方法 clone()

  • 构造函数 constructor(properties) 接受一个参数 properties,它是一个对象,用于初始化实例的属性。构造函数将 properties 对象中的属性复制到新创建的对象上
  • printProperties() 方法用于打印实例对象的所有属性
  • clone() 方法用于克隆当前对象,并返回一个新的对象实例。克隆方法会创建一个新的对象,并将当前对象的属性复制到新对象上,从而实现了对象的克隆


场景

  1. 对象创建开销大,但对象之间差异不大的情况下

    例如在游戏开发中,可以使用原型模式来创建大量的敌人对象,它们可能有不同的外观、属性,但是大部分行为和属性都是相似的,这时候可以通过克隆原型对象来节省创建对象的开销

  2. 需要避免使用复杂的继承结构

    有些场景中,类的继承结构非常复杂,使用原型模式可以简化对象的创建过程,避免过深的继承链带来的问题

  3. 动态加载类、对象或者模块

    在一些需要动态加载类或者模块的场景中,原型模式可以用来在运行时创建新的对象,而无需提前知道对象的具体类型

  4. 保护性拷贝

    有时候需要对对象进行保护性拷贝,以避免外部对原对象的修改影响到其他对象,原型模式提供了一种简单的方式来实现这一点


源代码