Rust语言之GoF设计模式:桥Bridge模式


桥Bridge模式是将业务逻辑或巨大的类型划分为独立的类型层次,可以独立开发。

案例,遥控器能遥控电视或收音机等设备,将遥控器与被遥控操作的设备分离,由于有共同的接口,相同的遥控器可以与不同的设备一起工作,估计是通过红外,这样的遥控器称为万能遥控器。

桥接模式能在改变甚至创建新的类情况下,不触及相对层次的代码。

先看看设备代码:

mod radio;
mod tv;

pub use radio::Radio;
pub use tv::Tv;

pub trait Device {
    fn is_enabled(&self) -> bool;
    fn enable(&mut self);
    fn disable(&mut self);
    fn volume(&self) -> u8;
    fn set_volume(&mut self, percent: u8);
    fn channel(&self) -> u16;
    fn set_channel(&mut self, channel: u16);
    fn print_status(&self);
}

这个设备能调节音量,改变频道
设备具体实现电视机:
tv.rs

use super::Device;

#[derive(Clone)]
pub struct Tv {
    on: bool,
    volume: u8,
    channel: u16,
}

impl Default for Tv {
    fn default() -> Self {
        Self {
            on: false,
            volume: 30,
            channel: 1,
        }
    }
}

impl Device for Tv {
    fn is_enabled(&self) -> bool {
        self.on
    }

    fn enable(&mut self) {
        self.on = true;
    }

    fn disable(&mut self) {
        self.on = false;
    }

    fn volume(&self) -> u8 {
        self.volume
    }

    fn set_volume(&mut self, percent: u8) {
        self.volume = std::cmp::min(percent, 100);
    }

    fn channel(&self) -> u16 {
        self.channel
    }

    fn set_channel(&mut self, channel: u16) {
        self.channel = channel;
    }

    fn print_status(&self) {
        println!("------------------------------------");
        println!(
"| I'm TV set.");
        println!(
"| I'm {}", if self.on { "enabled" } else { "disabled" });
        println!(
"| Current volume is {}%", self.volume);
        println!(
"| Current channel is {}", self.channel);
        println!(
"------------------------------------\n");
    }
}

收音机代码:radio.rs

下面是看看遥控器代码:
mod.rs

mod advanced;
mod basic;

pub use advanced::AdvancedRemote;
pub use basic::BasicRemote;

use crate::device::Device;

pub trait HasMutableDevice<D: Device> {
    fn device(&mut self) -> &mut D;
}

pub trait Remote<D: Device>: HasMutableDevice<D> {
    fn power(&mut self) {
        println!("Remote: power toggle");
        if self.device().is_enabled() {
            self.device().disable();
        } else {
            self.device().enable();
        }
    }

    fn volume_down(&mut self) {
        println!(
"Remote: volume down");
        let volume = self.device().volume();
        self.device().set_volume(volume - 10);
    }

    fn volume_up(&mut self) {
        println!(
"Remote: volume up");
        let volume = self.device().volume();
        self.device().set_volume(volume + 10);
    }

    fn channel_down(&mut self) {
        println!(
"Remote: channel down");
        let channel = self.device().channel();
        self.device().set_channel(channel - 1);
    }

    fn channel_up(&mut self) {
        println!(
"Remote: channel up");
        let channel = self.device().channel();
        self.device().set_channel(channel + 1);
    }
}

这个遥控器只对设备Device抽象接口操作:设置音量和更好频道。

下面是一个遥控器的一个基础实现:

basic.rs

use crate::device::Device;

use super::{HasMutableDevice, Remote};

pub struct BasicRemote<D: Device> {
    device: D,
}

impl<D: Device> BasicRemote<D> {
    pub fn new(device: D) -> Self {
        Self { device }
    }
}

impl<D: Device> HasMutableDevice<D> for BasicRemote<D> {
    fn device(&mut self) -> &mut D {
        &mut self.device
    }
}

impl<D: Device> Remote<D> for BasicRemote<D> {}

一个基本的遥控器可能只有两个按钮,但您可以使用额外的高级功能来扩展它,例如额外的电池或触摸屏。
还有一个高级遥控器实现:advanced.rs

以上准备好了桥模式的分散组件,下面看看如何在客户端将这些组件组合搭桥:
main.rs

mod device;
mod remotes;

use device::{Device, Radio, Tv};
use remotes::{AdvancedRemove, BasicRemote, HasMutableDevice, Remote};

fn main() {
    test_device(Tv::default());
    test_device(Radio::default());
}

fn test_device(device: impl Device + Clone) {
    println!("Tests with basic remote.");
    let mut basic_remote = BasicRemote::new(device.clone());
    basic_remote.power();
    basic_remote.device().print_status();

    println!(
"Tests with advanced remote.");
    let mut advanced_remote = AdvancedRemove::new(device);
    advanced_remote.power();
    advanced_remote.mute();
    advanced_remote.device().print_status();
}

上述代码运行输出:

Tests with basic remote.
Remote: power toggle
------------------------------------
| I'm TV set.
| I'm enabled
| Current volume is 30%
| Current channel is 1
------------------------------------

Tests with advanced remote.
Remote: power toggle
Remote: mute
------------------------------------
| I'm TV set.
| I'm enabled
| Current volume is 0%
| Current channel is 1
------------------------------------

Tests with basic remote.
Remote: power toggle
------------------------------------
| I'm radio.
| I'm enabled
| Current volume is 30%
| Current channel is 1
------------------------------------

Tests with advanced remote.
Remote: power toggle
Remote: mute
------------------------------------
| I'm radio.
| I'm enabled
| Current volume is 0%
| Current channel is 1
------------------------------------

当我们需要添加新的设备,如小米音响时,能不能通过这个万能遥控器实现遥控呢?首先需要打开万能遥控器App,找到相应的小米品牌下的音响,添加以后就可以了,上述代码也是实现现实生活中这种使用过程的思路。

桥模式一种用组合替代继承的做法,这里关键抽象是”遥控器“,这是一个耦合了设备Device的万能遥控器,按照继承思路,遥控器和设备Device应该各自有自己的接口类型,不应该混合在一起,因为它们是不同类型分类,不同的东西,但是这里遥控器将设备Device引入进来,是包含或组合了Device,相当于搭了一座桥。这样有新的设备Device类型也是通过这个桥接、组合思路实现音量和频道的调节。

桥模式 = 组合 + 面向接口编程