行为模式-中介者模式(The Mediator Pattern)

本文大部分内容翻译至《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")
        }
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容