设计模式笔记及Swift上的实现之四『PROTOTYPE(原型)』

意图

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

动机

我们希望我们的框架类和应用类进行解藕,但又必须知道如何创建应用类的对象。(很抽象=。=)

使用性

  • 当要实例化的类是在运行时刻指定是,例如,通过动态加载
  • 为了避免创建一个与产品类层次平行的工厂类层次
  • 当一个类的实例只能有几个不同状态组合中的一种。

结构

参与者

  • Prototype

—— 声明一个克隆自身的接口。

  • ConcretePrototype

—— 实现一个克隆自身的操作。

  • Client

—— 让一个原型克隆自身从而创建一个新的对象。

协助

  • 客户请求一个原型克隆自身。

效果

Prototype 有许多和 Abstract Factory 和 Builder 一样的效果: 它对客户隐藏了具体的产品类,因此减少了客户知道的名字的数目。

  1. 运行时刻增加和删除产品
  2. 改变值以指定新对象
  3. 改变结构以指定新对象
  4. 减少子类的构造
  5. 用类动态配置应用

实现原型是,要考虑的问题

  • 使用一个原型管理器

当一个系统的原型数目不固定时(也就是说,它们可以动态创建和销毁),要保持一个可用原型的注册表。
原型管理器是一个关联存储器,它返回一个与给定关键字匹配的原型。

  • 实现克隆操作
  • 初始化克隆对象

维基百科定义

写下了这么多笔记,这么多概念还是有点蒙圈 =。= ,于是又到维基百科搜了一下。

维基百科给出的定义:

原型模式是创建型模式的一种,其特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”,这个原型是可定制的。
原型模式多用于创建复杂的或者耗时的实例,因为这种情况下,复制一个已经存在的实例使程序运行更高效;或者创建值相等,只是命名不一样的同类数据

这就理解了!!其实就是 OC 中的 copy,C++中的拷贝构造函数 =。= 。

代码示例

我们来看看在 Swift 这么实现吧。其实 Swift 结构体本身就是值语义的 = = 。所以我用 Class ,根据书上的例子来个 Swift 版好了。

下面是我们 Swift 版本的 MazePrototypeFactory 。使用泛型可以让我们的 MazePrototypeFactory 适用于不同类型的 Door,Wall,Room 。 这里新的构造器只初始化它的原型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MazePrototypeFactory<W, D, R>: MazeFactory where W: WallType, D: DoorType, R: RoomType {
var prototypeMaze: Maze
var prototypeDoor: D
var prototypeWall: W
var prototypeRoom: R
func makeRoom(_ n: Int) -> RoomType {
return R(no: n)
}
func makeMaze() -> Maze {
return Maze()
}
init(m: Maze, d: D, w: W, r: R) {
prototypeMaze = m
prototypeDoor = d
prototypeWall = w
prototypeRoom = r
}
}

我们以克隆原型的方式定义,makeWallmakeDoor

1
2
3
4
5
6
7
8
9
10
11
12
extension MazePrototypeFactory {
func makeWall() -> WallType {
return prototypeWall.clone()
}
func makeDoor(r1: RoomType, r2: RoomType) -> DoorType {
let door = prototypeDoor.clone()
door.initialize(r1: r1, r2: r2)
return door
}
}

我们只需要使用基本的原型进行初始化,就可以由 MazePrototypeFactory 来创建一个原型的或缺省得迷宫。

1
2
3
4
let simpleMazeFactory = MazePrototypeFactory(m: Maze(), d: Door(), w: Wall(), r: Room())
let game = MazeGame.createMaze(mazeFactory: simpleMazeFactory)
print("\(game)")

打印结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
===========================
Maze room:
room_2 Room
north is Optional(Wall)
south is Optional(Wall)
east is Optional(Wall)
west is Optional(Door)
room_1 Room
north is Optional(Wall)
south is Optional(Wall)
east is Optional(Door)
west is Optional(Wall)
===========================

和之前的例子一样,我们又需要定义一个 Bombed 的迷宫。这次我们就不需要去定义一个 Bombed 的抽象工厂类了。我们以 BombedWallRoomWithABomb 作为原型就可以构造出一个 Bombed 的迷宫。

1
2
3
4
let bombMazeFactory = MazePrototypeFactory(m: Maze(), d: Door(), w: BombedWall(), r: RoomWithABomb())
let bombedGame = MazeGame.createMaze(mazeFactory: bombMazeFactory)
print("\(bombedGame)")

打印结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
===========================
Maze room:
room_2 RoomWithABomb Bombe is false
north is Optional(BombedWall Bombe is false)
south is Optional(BombedWall Bombe is false)
east is Optional(BombedWall Bombe is false)
west is Optional(Door)
room_1 RoomWithABomb Bombe is false
north is Optional(BombedWall Bombe is false)
south is Optional(BombedWall Bombe is false)
east is Optional(Door)
west is Optional(BombedWall Bombe is false)
===========================

总结

Prototype 一般可以和 Abstract Factory 一起使用,大量使用 Composite 和 Decorator 也可以 Prototype 获得收益。

附:Playground 代码

欢迎讨论、批评、指错。