本文大部分內(nèi)容翻譯至《Pro Design Pattern In Swift》By Adam Freeman狱杰,一些地方做了些許修改,并將代碼升級(jí)到了Swift2.0,翻譯不當(dāng)之處望多包涵。
中介者模式(The Mediator Pattern)
用一個(gè)中介者對(duì)象來封裝一系列的對(duì)象交互。中介者使得各對(duì)象不需要顯式地相互引用弹砚,從而使其松散耦合,而且可以獨(dú)立地改變它們之間的交互枢希。
示例工程
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類用來代表接近機(jī)場(chǎng)的一架飛機(jī)并且用Position結(jié)構(gòu)體來追蹤它的位置桌吃。當(dāng)然機(jī)場(chǎng)也可能有其它的飛機(jī)在接近,所以每一個(gè)Airplane對(duì)象都會(huì)追蹤其它接近的飛機(jī)苞轿,以免距離太近茅诱。然后我們看看main.swift中的應(yīng)用:
// 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()
運(yùn)行程序:
BA706: Position changed
BA706: Position changed
BA706: Position changed
BA706: Landed
理解中介者模式解決的問題
當(dāng)有幾架飛機(jī)同時(shí)接近機(jī)場(chǎng)時(shí),問題出現(xiàn)了搬卒。
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))
運(yùn)行程序:
BA706: Position changed
BA706: Position changed
BA706: Position changed
BA706: Landed
CX200: Too close! Abort!
這里只有三架飛機(jī)而已瑟俭,但是main.swift中的代碼已經(jīng)變得十分復(fù)雜臃腫了。
當(dāng)我們?cè)僭黾右患茱w機(jī)的時(shí)候秀睛,就更難受了尔当。
理解中介者模式
中介者模式通過引入一個(gè)中介者對(duì)象來簡化兩個(gè)對(duì)象或者更多對(duì)象之間的通信莲祸。為了使得對(duì)象間低耦合蹂安,中介者保持追蹤所有對(duì)象的狀態(tài)并且傳輸它們之間的通信椭迎。
實(shí)現(xiàn)中介者模式
中介者模式的核心是有一對(duì)協(xié)議:一個(gè)是定義同事對(duì)象的協(xié)議,另一個(gè)是中介者的協(xié)議田盈。
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中的應(yīng)用:
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))
運(yùn)行程序:
BA706: Position changed
BA706: Position changed
BA706: Position changed
BA706: Landed
CX200: Too close! Abort!
中介者并發(fā)保護(hù)
對(duì)中介者進(jìn)行并發(fā)保護(hù)保證了同事類集合不會(huì)損壞畜号。
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
}
}
我們想要能并發(fā)的去讀取同事類集合的數(shù)據(jù)直到要修改它,因此用了一個(gè)并行隊(duì)列并用同步的方式提交讀操作允瞧。用dispatch_barrier_sync方法去獲得唯一的訪問來修改简软。
并發(fā)保護(hù)同事類
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")
}
}
}