本文大部分内容翻译至《Pro Design Pattern In Swift》By Adam Freeman,一些地方做了些许修改,并将代码升级到了Swift2.0,翻译不当之处望多包涵。
中介者模式(The Mediator Pattern)
用一个中介者对象来封装一系列的对象交互。中介者使得各对象不需要显式地相互引用,从而使其松散耦合,而且可以独立地改变它们之间的交互。
示例工程
Xcode Command Line Tool 工程:
Airplane.swift
struct Position {
var distanceFromRunway:Int
var height:Int
}
func == (lhs:Airplane, rhs:Airplane) -> Bool {
return lhs.name == rhs.name
}
class Airplane : Equatable {
var name:String
var currentPosition:Position
private var otherPlanes:[Airplane]
init(name:String, initialPos:Position) {
self.name = name
self.currentPosition = initialPos
self.otherPlanes = [Airplane]()
}
func addPlanesInArea(planes:Airplane...) {
for plane in planes {
otherPlanes.append(plane)
}
}
func otherPlaneDidLand(plane:Airplane) {
if let index = otherPlanes.indexOf(plane) {
otherPlanes.removeAtIndex(index)
}
}
func otherPlaneDidChangePosition(plane:Airplane) -> Bool {
return plane.currentPosition.distanceFromRunway == self.currentPosition.distanceFromRunway && abs(plane.currentPosition.height - self.currentPosition.height) < 1000
}
func changePosition(newPosition:Position) {
self.currentPosition = newPosition
for plane in otherPlanes {
if (plane.otherPlaneDidChangePosition(self)) {
print("\(name): Too close! Abort!")
return
}
}
print("\(name): Position changed")
}
func land() {
self.currentPosition = Position(distanceFromRunway: 0, height: 0)
for plane in otherPlanes {
plane.otherPlaneDidLand(self)
}
print("\(name): Landed")
}
}
Airplane类用来代表接近机场的一架飞机并且用Position结构体来追踪它的位置。当然机场也可能有其它的飞机在接近,所以每一个Airplane对象都会追踪其它接近的飞机,以免距离太近。然后我们看看main.swift中的应用:
// initial setup
let british = Airplane(name: "BA706", initialPos: Position(distanceFromRunway: 11, height: 21000))
// plane approaches airport
british.changePosition(Position(distanceFromRunway: 8, height: 10000))
british.changePosition(Position(distanceFromRunway: 2, height: 5000))
british.changePosition(Position(distanceFromRunway: 1, height: 1000))
// plane lands
british.land()
运行程序:
BA706: Position changed
BA706: Position changed
BA706: Position changed
BA706: Landed
理解中介者模式解决的问题
当有几架飞机同时接近机场时,问题出现了。
main.swift
// initial setup
let british = Airplane(name: "BA706", initialPos:
Position(distanceFromRunway: 11, height: 21000))
// new plane arrives
let american = Airplane(name: "AA101", initialPos: Position(distanceFromRunway: 12, height: 22000))
british.addPlanesInArea(american)
american.addPlanesInArea(british)
// plane approaches airport
british.changePosition(Position(distanceFromRunway: 8, height: 10000))
british.changePosition(Position(distanceFromRunway: 2, height: 5000))
british.changePosition(Position(distanceFromRunway: 1, height: 1000))
// new plane arrives
let cathay = Airplane(name: "CX200", initialPos: Position(distanceFromRunway: 13, height: 22000))
british.addPlanesInArea(cathay)
american.addPlanesInArea(cathay)
cathay.addPlanesInArea(british, american)
// plane lands
british.land()
// plane moves too close
cathay.changePosition(Position(distanceFromRunway: 12, height: 22000))
运行程序:
BA706: Position changed
BA706: Position changed
BA706: Position changed
BA706: Landed
CX200: Too close! Abort!
这里只有三架飞机而已,但是main.swift中的代码已经变得十分复杂臃肿了。
当我们再增加一架飞机的时候,就更难受了。
理解中介者模式
中介者模式通过引入一个中介者对象来简化两个对象或者更多对象之间的通信。为了使得对象间低耦合,中介者保持追踪所有对象的状态并且传输它们之间的通信。
实现中介者模式
中介者模式的核心是有一对协议:一个是定义同事对象的协议,另一个是中介者的协议。
Mediator.swift
protocol Peer {
var name:String {get}
func otherPlaneDidChangePosition(position:Position) -> Bool
}
protocol Mediator {
func registerPeer(peer:Peer)
func unregisterPeer(peer:Peer)
func changePosition(peer:Peer, pos:Position) -> Bool
}
定义中介者类
下面我们定义中介者类:
Mediator.swift
......
class AirplaneMediator : Mediator {
private var peers:[String:Peer]
init() {
peers = [String:Peer]()
}
func registerPeer(peer: Peer) {
self.peers[peer.name] = peer
}
func unregisterPeer(peer: Peer) {
self.peers.removeValueForKey(peer.name)
}
func changePosition(peer:Peer, pos:Position) -> Bool {
for storedPeer in peers.values {
if (peer.name != storedPeer.name
&& storedPeer.otherPlaneDidChangePosition(pos)) {
return true
}
}
return false
}
}
.....
定义同事类
Airplane.swift
struct Position {
var distanceFromRunway:Int
var height:Int
}
class Airplane : Peer {
var name:String
var currentPosition:Position
var mediator:Mediator
init(name:String, initialPos:Position, mediator: Mediator) {
self.name = name
self.currentPosition = initialPos
self.mediator = mediator
mediator.registerPeer(self)
}
func otherPlaneDidChangePosition(position:Position) -> Bool {
return position.distanceFromRunway == self.currentPosition.distanceFromRunway && abs(position.height - self.currentPosition.height) < 1000
}
func changePosition(newPosition:Position) {
self.currentPosition = newPosition
if (mediator.changePosition(self, pos: self.currentPosition) == true) {
print("\(name): Too close! Abort!")
return
}
print("\(name): Position changed")
}
func land() {
self.currentPosition = Position(distanceFromRunway: 0, height: 0)
mediator.unregisterPeer(self)
print("\(name): Landed")
}
}
最后我们看main.swift中的应用:
let mediator:Mediator = AirplaneMediator()
// initial setup
let british = Airplane(name: "BA706", initialPos:
Position(distanceFromRunway: 11, height: 21000), mediator:mediator)
// new plane arrives
let american = Airplane(name: "AA101", initialPos: Position(distanceFromRunway: 12, height: 22000),
mediator:mediator)
// plane approaches airport
british.changePosition(Position(distanceFromRunway: 8, height: 10000))
british.changePosition(Position(distanceFromRunway: 2, height: 5000))
british.changePosition(Position(distanceFromRunway: 1, height: 1000))
// new plane arrives
let cathay = Airplane(name: "CX200", initialPos: Position(distanceFromRunway: 13, height: 22000),
mediator:mediator)
// plane lands
british.land()
// plane moves too close
cathay.changePosition(Position(distanceFromRunway: 12, height: 22000))
运行程序:
BA706: Position changed
BA706: Position changed
BA706: Position changed
BA706: Landed
CX200: Too close! Abort!
中介者并发保护
对中介者进行并发保护保证了同事类集合不会损坏。
Mediator.swift
import Foundation
protocol Peer {
var name:String {get}
func otherPlaneDidChangePosition(position:Position) -> Bool
}
protocol Mediator {
func registerPeer(peer:Peer)
func unregisterPeer(peer:Peer)
func changePosition(peer:Peer, pos:Position) -> Bool
}
class AirplaneMediator : Mediator {
private var peers:[String:Peer]
private let queue = dispatch_queue_create("dictQ", DISPATCH_QUEUE_CONCURRENT)
init() {
peers = [String:Peer]()
}
func registerPeer(peer: Peer) {
dispatch_barrier_sync(queue){[weak self] in
self!.peers[peer.name] = peer
}
}
func unregisterPeer(peer: Peer) {
dispatch_barrier_sync(queue){[weak self] in
self!.peers.removeValueForKey(peer.name)
}
}
func changePosition(peer:Peer, pos:Position) -> Bool {
var result = false
dispatch_sync(self.queue){[weak self] in
for storedPeer in self!.peers.values {
if (peer.name != storedPeer.name && storedPeer.otherPlaneDidChangePosition(pos)) {
result = true
}
}
}
return result
}
}
我们想要能并发的去读取同事类集合的数据直到要修改它,因此用了一个并行队列并用同步的方式提交读操作。用dispatch_barrier_sync方法去获得唯一的访问来修改。
并发保护同事类
Airplane.swift
import Foundation
struct Position {
var distanceFromRunway:Int
var height:Int
}
class Airplane : Peer {
var name:String
var currentPosition:Position
var mediator:Mediator
let queue = dispatch_queue_create("posQ", DISPATCH_QUEUE_CONCURRENT)
init(name:String, initialPos:Position, mediator: Mediator) {
self.name = name
self.currentPosition = initialPos
self.mediator = mediator
mediator.registerPeer(self)
}
func otherPlaneDidChangePosition(position:Position) -> Bool {
var result = false
dispatch_sync(queue){[weak self] in
result = position.distanceFromRunway == self!.currentPosition.distanceFromRunway && abs(position.height - self!.currentPosition.height) < 1000
}
return result
}
func changePosition(newPosition:Position) {
dispatch_barrier_sync(queue){[weak self] in
self!.currentPosition = newPosition
if (self!.mediator.changePosition(self!, pos: self!.currentPosition) == true) {
print("\(self!.name): Too close! Abort!")
return
}
print("\(self!.name): Position changed")
}
}
func land() {
dispatch_barrier_sync(queue){[weak self] in
self!.currentPosition = Position(distanceFromRunway: 0, height: 0)
self!.mediator.unregisterPeer(self!)
print("\(self!.name): Landed")
}
}
}