桥接模式

2022-03-01  本文已影响0人  闹鬼的金矿

先看一个简单的例子,比如现在有一个代表形状的类:

class Shape {}

形状可以包含正方形和圆形:

class SquareShape {}
class CircleShape {}

由于业务的发展,现在出现了红色和蓝色的图形,基于现在的情况进行扩展:

class RedSquareShape {}
class RedCircleShape {}
class BlueSquareShape {}
class BlueCircleShape {}

应该能感觉出来这种设计的问题了吧,随着Shape和Color两种业务类型不断增加,整体的类会快速增加,发展成M x N种情况。另外因为现在Shape和Color信息是合并在一个类里面,如果需要更改Shape或者Color相关的代码,是需要在同一个类里面进行修改的,增加了维护的成本,这种结构可以通过下图表示:

桥接模式-继承.png

而使用桥接模式首先需要将Shape类型和Color类型两种业务分开,形成能够独立发展的业务,相互之间的关系不在像之前一样耦合在一起的,现在是一种组合关系:

class Shape {
   var color: Color
}

class Color {}

class SquareShape: Shape {}
class CircleShape: Shape {}

class Red: Color {}
class Green: Color {}

Shape类型增加了对Color的引用,用来处理和Color之间的联系。现在这种结构下,不管是Shape还是Color增加新的类型,都只需要增加1种新类型了,并且由于使用了组合,现在Shape和Color之间可以减少继承带来的影响,独立发展自己的业务。

现在的结构如下图所示:

桥接模式-组合.png

Shape和Color实际上是对具体业务的抽象,Shape中包含了对Color的引用。具体的业务类型各自实现了Shape和Color接口,Circle, Square, Blue, Red都是实际的业务类型。

总之桥接模式想要实现的目的是将一个复杂的系统分解成两个能够独立演变的模块,模块之间能够通过引用来进行数据的传递和方法的调用。如果不考虑使用桥接模式,可能实现方式主要是继承,这样的处理会导致系统的复杂度随着子类的个数成倍数的增长,可能是m x n的关系,并且模块之间由于继承关系会相互耦合。通过桥接模式重构之后,模块之间相互独立,通过接口进行调用。系统的复杂度随着模块个数的增加线性增长,是m + n的关系。因为桥接模式它是通过组合的方式设计,所以两个模块之间的对象是可以按照需求来随意搭配,十分灵活。说的简单一点可能就是把部分继承关系重新设计成组合关系。

将Shape和Color的例子抽象一下,总结一个更能代表其结构的描述:

桥接模式.png

左侧是更加抽象的接口,它封装了业务流程,由它去决定某个业务的实现具体是调用哪些具体的接口,右侧则是具体实现业务逻辑的实现。它们的关系是左侧的抽象类型会依赖右侧更具体的类型。对于需求Client模块,它主要是和左侧的抽象模块进行交互和调用。而对于
抽象和实现部分,它们都可以根据需求继续有更多的派生类。在实际的业务场景中,又左右两侧不同的类型进行组合来满足需求。

下面是一个通过不同的Control和Device进行组合,生成不同遥控器的例子:

先定义Control和Device各自的协议,这里并不是必须要定义成Protocol,基类也是可以的,都可以说明问题。重点在于理解Control和
Device的关系和各自的作用。

protocol Device {
    func getVolumn() -> Int;
    func setVolumn(volumn: Int);
    func getChannel() -> Int;
    func setChannel(channel: Int);
    func enable();
    func disable();
    func isEnable() -> Bool;
    func printSelf();
}

protocol RemoteControl {
    func setupDevice(device: Device);
    func powChange();
    func channelUp();
    func channelDown();
    func volumnUp();
    func volumnDown();
}

具体Control的实现,它有一个对Device的依赖,具体的功能还需要通过Device来实现的。它主要实现了对外的接口定义和流程的封装。
这里分别定义了DeviceControl和AdvanceControl,AdvanceControl比DeviceControl具有更多的功能。

class DeviceControl: RemoteControl {
    
    var device: Device?
    
    func setupDevice(device: Device) {
        self.device = device;
    }
    
    func powChange() {
        guard let d = self.device else {
            return;
        }
        if d.isEnable() {
            d.disable()
        } else {
            d.enable()
        }
    }
    
    func channelUp() {
        guard let d = self.device else {
            return;
        }
        let c = d.getChannel();
        d.setChannel(channel: c + 1);
    }
    
    func channelDown() {
        guard let d = self.device else {
            return
        }
        let c = d.getChannel();
        d.setChannel(channel: c - 1);
    }
    
    func volumnUp() {
        guard let d = self.device else {
            return
        }
        let v = d.getVolumn()
        d.setVolumn(volumn: v + 1)
    }
    
    func volumnDown() {
        guard let d = self.device else {
            return
        }
        let v = d.getVolumn()
        d.setVolumn(volumn: v - 1)
    }
}

class AdvanceControl: DeviceControl {
    func mute() {
        guard let d = self.device else {
            return
        }
        d.setVolumn(volumn: 0)
    }
}

接着是两个具体Device的定义:FM和TV。增加了PrintSelf方法用来方便后面在Control改变其状态后,通过打印自己的方式查看其状态。

class FM: Device {
    
    var volumn: Int = 0;
    var channel: Int = 1;
    var enabled: Bool = true;
    
    func getVolumn() -> Int {
        return self.volumn
    }
    
    func setVolumn(volumn: Int) {
        self.volumn = volumn
    }
    
    func getChannel() -> Int {
        return self.channel
    }
    
    func setChannel(channel: Int) {
        self.channel = channel
    }
    
    func enable() {
        self.enabled = true
    }
    
    func disable() {
        self.enabled = false
    }
    
    func isEnable() -> Bool {
        return self.enabled
    }
    
    func printSelf() {
        print("收音机 音量是: \(self.volumn), 频道是: \(self.channel), 是否开机: \(self.enabled)")
    }
}

class TV: Device {
    
    var volumn: Int = 0;
    var channel: Int = 1;
    var enabled: Bool = true;
    
    func getVolumn() -> Int {
        return self.volumn
    }
    
    func setVolumn(volumn: Int) {
        self.volumn = volumn
    }
    
    func getChannel() -> Int {
        return self.channel
    }
    
    func setChannel(channel: Int) {
        self.channel = channel
    }
    
    func enable() {
        self.enabled = true
    }
    
    func disable() {
        self.enabled = false
    }
    
    func isEnable() -> Bool {
        return self.enabled
    }
    
    func printSelf() {
        print("电视机 音量是: \(self.volumn), 频道是: \(self.channel), 是否开机: \(self.enabled)")
    }
}

最后是测试代码,基本上就是Client中的业务逻辑:

func testBridge() {
    
    let control = DeviceControl()
    let advanceControl = AdvanceControl()
    let tv = TV()
    let fm = FM()
    control.setupDevice(device: tv)
    control.volumnUp()
    control.channelUp()
    control.powChange()
    tv.printSelf()
    
    control.setupDevice(device: fm)
    control.volumnUp()
    control.channelUp()
    control.powChange()
    fm.printSelf()
    
    advanceControl.setupDevice(device: fm)
    advanceControl.volumnUp()
    advanceControl.channelUp()
    advanceControl.powChange()
    advanceControl.mute()
    fm.printSelf()
    
    advanceControl.setupDevice(device: tv)
    advanceControl.volumnUp()
    advanceControl.channelUp()
    advanceControl.powChange()
    advanceControl.mute()
    tv.printSelf()
}

另外,通过桥接模式还可以在运行中动态的去改变一些实现,其实也是因为使用
了组合的原因,替换具体的实现类就可以了。

Reference: Dive Into Design Patterns

上一篇 下一篇

猜你喜欢

热点阅读