最近开始在研读《设计模式》一书,书中主要是以 C++ 和 Smalltalk 为示例。所以我准备写一系列的读书笔记,并尝试将 23 种设计模式通过 Swift 实现,从而加深自己的理解。
提供一个创建一系列相关或互相依赖对象的接口,而无需指定它们具体的类。
存在多组功能相似的组件,但用户层(客户)不需要关心组件之间的差异,用户层只与抽象类定义的接口交互
书中以一个迷宫游戏为例子。
例子: 为电脑游戏创建一个迷宫。迷宫定义了一系列房间,一个房间知道他的邻居;可能的邻居要么是另一个房间,要么是一堵墙、或者是另一个房间的门。
enumDirection{ case north, south, east, west }
MapSite
表示地图上的元素 protocolMapSite{ funcenter() }
房间
、 墙
、 门
三种类型 protocol WallType: MapSite { } protocol DoorType: MapSite { } protocol WallType: MapSite { }
这三种类型都属于地图上的元素,所以继承 MapSite
协议。
房间
、 墙
、 门
structRoom:RoomType{ var sides: [MapSite?] = [nil, nil, nil, nil] var roomNo: Int init(no: Int) { roomNo = no } } structWall:WallType{ } structDoor:DoorType{ var isOpen = false var room1: RoomType var room2: RoomType init(r1: RoomType, r2: RoomType) { room1 = r1 room2 = r2 } mutating funcotherSide(form room: RoomType) -> RoomType { isOpen = true if (room == room1) { return room2 } else { return room1 } } }
在 Swift 中我更喜欢使用 struct
代替 class
,虽然 struct
无法继承。但我可以使用 protocol
实现部分继承的需求。
protocolMazeFactory{ associatedtype RoomMazeType: RoomType associatedtype WallMazeType: WallType associatedtype DoorMazeType: DoorType funcmakeMaze() -> Maze funcmakeWall() -> WallMazeType funcmakeRoom(_n: Int) -> RoomMazeType funcmakeDoor(r1: RoomMazeType, r2: RoomMazeType) -> DoorMazeType } extensionMazeFactory{ funcmakeMaze() -> Maze { return Maze() } funcmakeDoor(r1: Room, r2: Room) -> Door { return Door(r1: r1, r2: r2) } funcmakeRoom(_n: Int) -> Room { return Room(no: n) } funcmakeWall() -> Wall { return Wall() } }
工厂在书中 C++ 的例子中使用抽象类实现, Swift 没有抽象类,但 Swift 中可以通过 protocol
实现抽象类的功能。通过协议扩展提供默认的实现。
好了现在我们可以尝试通过我们的代码来生成一个普通的迷宫了。
struct MazeGame { static func createMaze<T: MazeFactory>(mazeFactory: T) -> Maze { var maze = mazeFactory.makeMaze() var r1 = mazeFactory.makeRoom(1) var r2 = mazeFactory.makeRoom(2) let theDoor = mazeFactory.makeDoor(r1: r1, r2: r2) r1.setSide(dect: .north, site: mazeFactory.makeWall()) r1.setSide(dect: .east, site: theDoor) r1.setSide(dect: .south, site: mazeFactory.makeWall()) r1.setSide(dect: .west, site: mazeFactory.makeWall()) r2.setSide(dect: .north, site: mazeFactory.makeWall()) r2.setSide(dect: .east, site: mazeFactory.makeWall()) r2.setSide(dect: .south, site: mazeFactory.makeWall()) r2.setSide(dect: .west, site: theDoor) maze.addRoom(room: r1) maze.addRoom(room: r2) return maze } } // 使用工厂构建普通的迷宫 var normalMazeFactory = NormalMazeFactory() var normalMaze = MazeGame.createMaze(mazeFactory: normalMazeFactory) print(normalMaze)
打印结果:
=========================== 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) ===========================
嗯,感觉还不错。
新的需求来了,产品经理告诉我们普通迷宫用户玩腻了,我们要新增一个魔法迷宫。魔法迷宫内有 发光的房间
和 需要咒语才能打开的门
。
发光的房间
和 需要咒语才能打开的门
structEnchantedRoom: RoomType{ ...... } structDoorNeedingSpell: DoorType{ ...... }
structEnchantedMazeFactory:MazeFactory{ typealias RoomMazeType = EnchantedRoom typealias DoorMazeType = DoorNeedingSpell funcmakeDoor(r1: EnchantedRoom, r2: EnchantedRoom) -> DoorNeedingSpell { return DoorNeedingSpell(r1: r1, r2: r2) } funcmakeRoom(_n: Int) -> RoomMazeType { return EnchantedRoom(n, spell: Spell()) } }
var enchantedMazeFactory = EnchantedMazeFactory() var enchantedMaze = MazeGame.createMaze(mazeFactory: enchantedMazeFactory) print(enchantedMaze)
打印结果:
=========================== Maze room: room_2 EnchantedRoom north is Optional(Wall) south is Optional(Wall) east is Optional(Wall) west is Optional(DoorNeedingSpell) room_1 EnchantedRoom north is Optional(Wall) south is Optional(Wall) east is Optional(DoorNeedingSpell) west is Optional(Wall) ===========================
由于用户层只和 MazeFactory
的接口交互,所以完全不会感知到 EnchantedMazeFactory
和 NormalMazeFactory
的变化。
又有新的需求!!要创建一个有炸弹的迷宫,我们需要一个有炸弹的房间,如果炸弹爆炸则会炸毁房间的墙。我们通过创建一个新的炸弹迷宫工厂,同样可以轻松的完成任务。Let to do.
有炸弹的房间
和 会被炸毁的墙
structRoomWithABomb:RoomType{ var sides: [MapSite?] = [nil, nil, nil, nil] var roomNo: Int var isBombe: Bool init(_ n: Int, isBombe: Bool) { roomNo = n self.isBombe = isBombe } } structBombedWall:WallType{ var isBombed: Bool init(_ isBombe: Bool) { self.isBombed = isBombe } }
structBombedMazeFactory:MazeFactory{ typealias WallMazeType = BombedWall typealias RoomMazeType = RoomWithABomb funcmakeWall() -> BombedWall { return BombedWall(false) } funcmakeRoom(_n: Int) -> RoomWithABomb { return RoomWithABomb(n, isBombe: false) } funcmakeDoor(r1: RoomWithABomb, r2: RoomWithABomb) -> Door { return Door(r1: r1, r2: r2) } }
var bombedMazeFactory = BombedMazeFactory() var bombedMaze = MazeGame.createMaze(mazeFactory: bombedMazeFactory) print(bombedMaze)
打印结果:
=========================== 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) ===========================
添加再多的迷宫,都不会对用户的代码造成影响。我们可以根据不同的配置,创建不同的工厂,但用户对此并无感知。
抽象工厂模式适用于用户不需要知道具体的类型,只需要和协商好的接口交互。
附: Playground 代码
欢迎讨论、批评、指错。