意图
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
动机
我们希望我们的框架类和应用类进行解藕,但又必须知道如何创建应用类的对象。(很抽象=。=)
使用性
- 当要实例化的类是在运行时刻指定是,例如,通过动态加载
- 为了避免创建一个与产品类层次平行的工厂类层次
- 当一个类的实例只能有几个不同状态组合中的一种。
结构
参与者
- Prototype
—— 声明一个克隆自身的接口。
- ConcretePrototype
—— 实现一个克隆自身的操作。
- Client
—— 让一个原型克隆自身从而创建一个新的对象。
协助
- 客户请求一个原型克隆自身。
效果
Prototype 有许多和 Abstract Factory 和 Builder 一样的效果: 它对客户隐藏了具体的产品类,因此减少了客户知道的名字的数目。
- 运行时刻增加和删除产品
- 改变值以指定新对象
- 改变结构以指定新对象
- 减少子类的构造
- 用类动态配置应用
实现原型是,要考虑的问题
- 使用一个原型管理器
当一个系统的原型数目不固定时(也就是说,它们可以动态创建和销毁),要保持一个可用原型的注册表。
原型管理器是一个关联存储器,它返回一个与给定关键字匹配的原型。
- 实现克隆操作
- 初始化克隆对象
维基百科定义
写下了这么多笔记,这么多概念还是有点蒙圈 =。= ,于是又到维基百科搜了一下。
维基百科给出的定义:
原型模式是创建型模式的一种,其特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”,这个原型是可定制的。
原型模式多用于创建复杂的或者耗时的实例,因为这种情况下,复制一个已经存在的实例使程序运行更高效;或者创建值相等,只是命名不一样的同类数据
这就理解了!!其实就是 OC 中的 copy,C++中的拷贝构造函数 =。= 。
代码示例
我们来看看在 Swift 这么实现吧。其实 Swift 结构体本身就是值语义的 = = 。所以我用 Class ,根据书上的例子来个 Swift 版好了。
下面是我们 Swift 版本的 MazePrototypeFactory
。使用泛型可以让我们的 MazePrototypeFactory
适用于不同类型的 Door,Wall,Room 。 这里新的构造器只初始化它的原型。
class MazePrototypeFactory
: 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 } }
我们以克隆原型的方式定义,makeWall
和 makeDoor
。
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 来创建一个原型的或缺省得迷宫。
let simpleMazeFactory = MazePrototypeFactory(m: Maze(), d: Door(), w: Wall(), r: Room())
let game = MazeGame.createMaze(mazeFactory: simpleMazeFactory)
print("\(game)")
打印结果:
===========================
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 的抽象工厂类了。我们以 BombedWall
和 RoomWithABomb
作为原型就可以构造出一个 Bombed 的迷宫。
let bombMazeFactory = MazePrototypeFactory(m: Maze(), d: Door(), w: BombedWall(), r: RoomWithABomb())
let bombedGame = MazeGame.createMaze(mazeFactory: bombMazeFactory)
print("\(bombedGame)")
打印结果:
===========================
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 获得收益。
欢迎讨论、批评、指错。