结构模式-桥接模式(The Bridge Pattern)

本文大部分内容翻译至《Pro Design Pattern In Swift》By Adam Freeman,一些地方做了些许修改,并将代码升级到了Swift2.0,翻译不当之处望多包涵。

桥接模式(The Bridge Pattern)

桥接模式把事物对象和其具体行为、具体特征分离开来,使它们可以各自独立的变化。事物对象仅是一个抽象的概念。如“圆形”、“三角形”归于抽象的“形状”之下,而“画圆”、“画三角”归于实现行为的“画图”类之下,然后由“形状”调用“画图”。


示例工程

OS X Command Line Tool工程:

Comms.swift

protocol ClearMessageChannel {
    func send(message:String)
}

protocol SecureMessageChannel {
    func sendEncryptedMessage(encryptedText:String)
}

class Communicator {
    private let clearChannel:ClearMessageChannel
    private let secureChannel:SecureMessageChannel
    
    init (clearChannel:ClearMessageChannel, secureChannel:SecureMessageChannel) {
        self.clearChannel = clearChannel
        self.secureChannel = secureChannel
    }
    
    func sendCleartextMessage(message:String) {
        self.clearChannel.send(message);
    }
    
    func sendSecureMessage(message:String) {
        self.secureChannel.sendEncryptedMessage(message)
    }
}

Communicator提供了允许传送标准信息和安全信息的方法。操作这些信息的机制是 ClearMessageChannel协议和SecureMessageChannel 协议。

这里我们将创建两种不同的网络机制来传输信息:有线传输和无线传输。

Channels.swift

class Landline : ClearMessageChannel {
    func send(message: String) {
        print("Landline: \(message)")
    }
}

class SecureLandLine : SecureMessageChannel {
    func sendEncryptedMessage(message: String) {
        print("Secure Landline: \(message)")
    }
}

class Wireless : ClearMessageChannel {
    func send(message: String) {
        print("Wireless: \(message)")
    }
}

class SecureWireless : SecureMessageChannel {
    func sendEncryptedMessage(message: String) {
        print("Secure Wireless: \(message)")
    }
}

然后我们看main.swift:

main.swift

var clearChannel = Landline()
var secureChannel = SecureLandLine()
var comms = Communicator(clearChannel: clearChannel, secureChannel: secureChannel)
comms.sendCleartextMessage("Hello!")
comms.sendSecureMessage("This is a secret")

运行程序:

Landline: Hello!
Secure Landline: This is a secret

理解桥接模式解决的问题


从上图可以看出,问题在于每一次我们增加一个新的机能或者一个新的平台,都会导致实现类的数量急剧的增加。比如我们再增加一个Chanel,那么实现类将会增加到6个。这就是所谓的类爆炸式层级问题。


理解桥接模式

桥接模式从实现中分离出一个抽象,这样他们都能各自独立的变化。

Tip:分离共通和特定功能是最有效的办法。

在示例中,特定功能是每一个平台使用一个特殊的网络来传输信息,共通功能是传输信息的准备。第一步是定义每一个协议---信息和传输。然后是创建实现类。



实现桥接模式

处理信息

首先我们处理共通功能--信息。

Messages.swift

import Foundation
protocol Message {
    init (message:String)
    func prepareMessage()
    var contentToSend:String { get }
}

class ClearMessage : Message {
    private var message:String
        
    required init(message:String) {
        self.message = message
    }
        
    func prepareMessage() {
        // no action required
    }
        
    var contentToSend:String {
        return message
    }
}

class EncryptedMessage : Message {
    private var clearText:String
    private var cipherText:String?
    
    required init(message:String) {
        self.clearText = message
    }
    
    func prepareMessage() {
       cipherText = String(clearText.characters.reverse())
       
    }
    
    var contentToSend:String {
        return cipherText!
    }
}

处理传输

Channels.swift

protocol Channel {
    func sendMessage(msg:Message)
}

class LandlineChannel : Channel {
    func sendMessage(msg: Message) {
        print("Landline: \(msg.contentToSend)")
    }
}

class WirelessChannel : Channel {
    
    func sendMessage(msg: Message) {
        print("Wireless: \(msg.contentToSend)")
    }
}

创建桥

最后,我们需要创建一个类来将Communicator类和Message协议,Channel协议连接起来。

Bridge.swift

class CommunicatorBridge : ClearMessageChannel, SecureMessageChannel {
    private var channel:Channel
    
    init(channel:Channel) {
        self.channel = channel
    }
    
    func send(message: String) {
        let msg = ClearMessage(message: message);
        sendMessage(msg)
    }
    
    func sendEncryptedMessage(encryptedText: String) {
        let msg = EncryptedMessage(message: encryptedText)
        sendMessage(msg)
    }
    
    private func sendMessage(msg:Message) {
        msg.prepareMessage()
        channel.sendMessage(msg)
    }
}

最后我们修改main.swift:

main.swift

var bridge = CommunicatorBridge(channel: LandlineChannel())
var comms = Communicator(clearChannel: bridge, secureChannel: bridge)
comms.sendCleartextMessage("Hello!")
comms.sendSecureMessage("This is a secret")

运行程序:

Landline: Hello!
Landline: terces a si sihT

增加新的信息和传输

Comms.swift

protocol ClearMessageChannel {
    func send(message:String)
}

protocol SecureMessageChannel {
    func sendEncryptedMessage(encryptedText:String)
}

protocol PriorityMessageChannel {
    func sendPriority(message:String)
}

class Communicator {
    private let clearChannel:ClearMessageChannel
    private let secureChannel:SecureMessageChannel
    private let priorityChannel:PriorityMessageChannel
    
    init (clearChannel:ClearMessageChannel, secureChannel:SecureMessageChannel, priorityChannel:PriorityMessageChannel) {
        self.clearChannel = clearChannel
        self.secureChannel = secureChannel
        self.priorityChannel = priorityChannel
    }
    
    func sendCleartextMessage(message:String) {
        self.clearChannel.send(message)
    }
    
    func sendSecureMessage(message:String) {
        self.secureChannel.sendEncryptedMessage(message)
    }
    
    func sendPriorityMessage(message:String) {
        self.priorityChannel.sendPriority(message)
    }
}

如果不使用桥接模式,那么新增一个信息类型将要增加5个类。请看下图:



而在桥接模式下,添加相同的信息和传输只需要新增两个类。


NewFeatures.swift

class SatelliteChannel : Channel {
    func sendMessage(msg: Message) {
        print("Satellite: \(msg.contentToSend)")
    }
}

class PriorityMessage : ClearMessage {
    override var contentToSend:String {
        return "Important: \(super.contentToSend)"
    }
}

接下来我们更新 Communicator 类:

Bridge.swift

class CommunicatorBridge : ClearMessageChannel, SecureMessageChannel,PriorityMessageChannel{
    private var channel:Channel
    
    init(channel:Channel) {
        self.channel = channel
    }
    
    func send(message: String) {
        let msg = ClearMessage(message: message);
        sendMessage(msg)
    }
    
    func sendEncryptedMessage(encryptedText: String) {
        let msg = EncryptedMessage(message: encryptedText)
        sendMessage(msg)
    }
    
    func sendPriority(message: String) {
        sendMessage(PriorityMessage(message: message))
    }
    
    private func sendMessage(msg:Message) {
        msg.prepareMessage()
        channel.sendMessage(msg)
    }
}

最后main.swift:

main.swift

var bridge = CommunicatorBridge(channel: SatelliteChannel())
var comms = Communicator(clearChannel: bridge,secureChannel: bridge,
    priorityChannel: bridge)

comms.sendCleartextMessage("Hello!")
comms.sendSecureMessage("This is a secret")
comms.sendPriorityMessage("This is important")

运行程序:

Satellite: Hello!
Satellite: terces a si sihT
Satellite: Important: This is important

桥接模式的变形

最简单的变形就是和工厂模式结合,这样特定部分的选择就会被隐藏起来。

Channels.swift

class Channel {
    enum Channels {
        case Landline
        case Wireless
        case Satellite
    }
    
    class func getChannel(channelType:Channels) -> Channel {
        switch channelType {
            case .Landline:
                return LandlineChannel()
            case .Wireless:
                return WirelessChannel()
            case .Satellite:
                return SatelliteChannel()
        }
    }
    
    func sendMessage(msg:Message) {
            fatalError("Not implemented")
    }
}

class LandlineChannel : Channel {
    override func sendMessage(msg: Message) {
        print("Landline: \(msg.contentToSend)")
    }
}

class WirelessChannel : Channel {
    
    override func sendMessage(msg: Message) {
        print("Wireless: \(msg.contentToSend)")
    }
}

然后是NewFeatures.swift

class SatelliteChannel : Channel {
    override func sendMessage(msg: Message) {
        print("Satellite: \(msg.contentToSend)")
    }
}

class PriorityMessage : ClearMessage {
    override var contentToSend:String {
        return "Important: \(super.contentToSend)"
    }
}

修改Bridge.swift中的初始化方法:

Bridge.swift

...
init(channel:Channel.Channels) {
        self.channel = Channel.getChannel(channel)
    }
...

最后更新main.swift:

var bridge = CommunicatorBridge(channel:Channel.Channels.Satellite)
var comms = Communicator(clearChannel: bridge,secureChannel: bridge,
    priorityChannel: bridge)

comms.sendCleartextMessage("Hello!")
comms.sendSecureMessage("This is a secret")
comms.sendPriorityMessage("This is important")

运行程序,得到结果和上面的一样:

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

推荐阅读更多精彩内容