本文大部分内容翻译至《Pro Design Pattern In Swift》By Adam Freeman,一些地方做了些许修改,并将代码升级到了Swift2.0,翻译不当之处望多包涵。
外观模式(The Façade Pattern)
外观模式,为子系统中的一组接口提供一个一致的界面,定义一个高层接口,这个接口使得这一子系统更加容易使用。
示例工程
Xcode OS X Command Line Tool 工程:
TreasureMap.swift
class TreasureMap {
enum Treasures {
case GALLEON
case BURIED_GOLD
case SUNKEN_JEWELS
}
struct MapLocation {
let gridLetter: Character
let gridNumber: UInt
}
func findTreasure(type:Treasures) -> MapLocation {
switch type {
case .GALLEON:
return MapLocation(gridLetter: "D", gridNumber: 6)
case .BURIED_GOLD:
return MapLocation(gridLetter: "C", gridNumber: 2)
case .SUNKEN_JEWELS:
return MapLocation(gridLetter: "F", gridNumber: 12)
}
}
}
TreasureMap类定义了一个 findTreasure 方法,方法接受一个内置的Treasures枚举作为参数并返回一个 MapLocation。
PirateShip.swift
import Foundation
class PirateShip {
struct ShipLocation {
let NorthSouth:Int
let EastWest:Int
}
var currentPosition:ShipLocation
var movementQueue = dispatch_queue_create("shipQ", DISPATCH_QUEUE_SERIAL)
init() {
currentPosition = ShipLocation(NorthSouth: 5, EastWest: 5)
}
func moveToLocation(location:ShipLocation, callback:(ShipLocation) -> Void) {
dispatch_async(movementQueue){
() in
self.currentPosition = location
callback(self.currentPosition)
}
}
}
PirateShip 代表一艘可以移动到新地点的船。内置结构题ShipLocation代表船的地点。因为船移动到新的地点需要时间,所以 moveToLocation方法是异步的并且用一个回调方法callback来通知船到新的地点。
PirateCrew.swift
import Foundation
class PirateCrew {
let workQueue = dispatch_queue_create("crewWorkQ", DISPATCH_QUEUE_SERIAL)
enum Actions {
case ATTACK_SHIP
case DIG_FOR_GOLD
case DIVE_FOR_JEWELS
}
func performAction(action:Actions, callback:(Int) -> Void) {
dispatch_async(workQueue){ () in
var prizeValue = 0
switch (action) {
case .ATTACK_SHIP:
prizeValue = 10000
case .DIG_FOR_GOLD:
prizeValue = 5000
case .DIVE_FOR_JEWELS:
prizeValue = 1000
}
callback(prizeValue)
}
}
}
PirateCrew 类代表全体船员,可以通过performAction 方法为他们指定任务。每种任务有不同的奖励。
理解外观模式解决的问题
海盗为了获取财宝,那么三个类必须一起使用。TreasureMap类提供宝藏的位置信息。 PirateShip类提供了将劳动力移动到宝藏地点的方式。 PirateCrew提供了挖掘宝藏的劳动力。
这些类必须按特定顺序使用。
为了让情况更糟,这三个类使用了不同的数据类型来代表它们的输入和输出。
然后,我们看main.swift:
import Foundation
let map = TreasureMap()
let ship = PirateShip()
let crew = PirateCrew()
let treasureLocation = map.findTreasure(TreasureMap.Treasures.GALLEON)
// convert from map to ship coordinates
let sequence:[Character] = ["A", "B", "C", "D", "E", "F", "G"]
let eastWestPos = sequence.indexOf(treasureLocation.gridLetter)
let shipTarget = PirateShip.ShipLocation(NorthSouth:
Int(treasureLocation.gridNumber), EastWest: eastWestPos!)
// relocate ship
ship.moveToLocation(shipTarget){
location in
// get the crew to work
crew.performAction(PirateCrew.Actions.ATTACK_SHIP){ prize in
print("Prize: \(prize) pieces of eight")
}
}
NSFileHandle.fileHandleWithStandardInput().availableData
运行程序:
Prize: 10000 pieces of eight
理解外观模式
外观模式通过创建一个外观类来将复杂的逻辑集合起来对外提供只提供一个简单的接口。
实现外观模式
外观模式通过提供一个外观类来提供API的接口,而且不能暴露相关类的具体详细。
Facade.swift
import Foundation
enum TreasureTypes {
case SHIP
case BURIED
case SUNKEN
}
class PirateFacade {
private let map = TreasureMap()
private let ship = PirateShip()
private let crew = PirateCrew()
func getTreasure(type:TreasureTypes) -> Int? {
var prizeAmount:Int?
// select the treasure type
var treasureMapType:TreasureMap.Treasures
var crewWorkType:PirateCrew.Actions
switch (type) {
case .SHIP:
treasureMapType = TreasureMap.Treasures.GALLEON
crewWorkType = PirateCrew.Actions.ATTACK_SHIP
case .BURIED:
treasureMapType = TreasureMap.Treasures.BURIED_GOLD
crewWorkType = PirateCrew.Actions.DIG_FOR_GOLD
case .SUNKEN:
treasureMapType = TreasureMap.Treasures.SUNKEN_JEWELS
crewWorkType = PirateCrew.Actions.DIVE_FOR_JEWELS
}
let treasureLocation = map.findTreasure(treasureMapType)
// convert from map to ship coordinates
let sequence:[Character] = ["A", "B", "C", "D", "E", "F", "G"]
let eastWestPos = sequence.indexOf(treasureLocation.gridLetter)
let shipTarget = PirateShip.ShipLocation(NorthSouth:
Int(treasureLocation.gridNumber), EastWest: eastWestPos!)
let semaphore = dispatch_semaphore_create(0)
// relocate ship
ship.moveToLocation(shipTarget){location in
self.crew.performAction(crewWorkType){prize in
prizeAmount = prize
dispatch_semaphore_signal(semaphore)
}
}
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
return prizeAmount
}
}
接着我们可以修改main.swift:
let facade = PirateFacade()
let prize = facade.getTreasure(TreasureTypes.SHIP)
if (prize != nil) {
print("Prize: \(prize!) pieces of eight")
}
运行程序,可以看出和上面的结果相同:
Prize: 10000 pieces of eight