桥接模式

67 阅读5分钟

1. 一句话介绍

桥接模式是一种结构型设计模式,它将抽象部分与其实现部分分离,使它们可以独立地变化,它的主要目的是通过组合建立两个类之间的联系,而不是继承的方式。

2. 详细介绍

桥接模式的主要优点是分离抽象接口及其实现部分,是比多继承方案更好的解决方法,桥接模式还提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。

2.1 模式结构

桥接模式包含以下主要角色:

  • 抽象部分(Abstraction):定义了客户端使用的接口,依赖于实现部分。
  • 实现部分(Implementation):为所有具体实现声明一个通用接口,抽象部分通过这个接口与实现对象交互。
  • 具体实现(Concrete Implementations):提供实现部分接口的具体实现。
  • 精确抽象(Refined Abstraction):如果抽象部分有多个变体,可以为每个变体创建一个精确抽象。
  • 客户端(Client):与抽象部分交互,需要将抽象对象与一个实现对象关联起来。

2.2 使用桥接模式的相关对象

客户端首先创建一个实现对象,然后创建一个抽象对象,并将实现对象传递给抽象对象。之后,客户端通过抽象对象的方法进行操作,这些操作将委托给实现对象。

3. 桥接模式的优缺点以及使用场景

  • 优点

    • 可以创建与平台无关的类和程序。
    • 客户端代码仅与高层抽象部分进行互动,不会接触到平台的详细信息。
    • 符合开闭原则,可以新增抽象部分和实现部分,且它们之间不会相互影响。
    • 符合单一职责原则,抽象部分专注于处理高层逻辑,实现部分处理平台细节。
  • 缺点

    • 对高内聚的类使用该模式可能会让代码更加复杂。
  • 使用场景

    • 当你想拆分或重组一个具有多重功能的庞杂类时。
    • 当你希望在几个独立维度上扩展一个类时。
    • 当你需要在运行时切换不同实现方法时。
  1. 使用Python实现桥接模式(以万能遥控器为例子)

    # 实现部分接口
    class Device:
        def enable(self):
            pass
        
        def disable(self):
            pass
        
        def get_volume(self):
            pass
        
        def set_volume(self, percent):
            pass
        
        def get_channel(self):
            pass
        
        def set_channel(self, channel):
            pass
        
        def is_enabled(self):
            pass
        
    # 具体实现
    class Tv(Device):
        def enable(self):
            print("TV is on")
        
        def disable(self):
            print("TV is off")
        
        def get_volume(self):
            return 10
        
        def set_volume(self, percent):
            print(f"Setting volume to {percent}")
        
        def get_channel(self):
            return 1
        
        def set_channel(self, channel):
            print(f"Setting channel to {channel}")
        
        def is_enabled(self):
            return True
    
    # 抽象部分
    class RemoteControl:
        def __init__(self, device):
            self.device = device
    
        def toggle_power(self):
            if self.device.is_enabled():
                self.device.disable()
            else:
                self.device.enable()
        
        def volume_down(self):
            current_volume = self.device.get_volume()
            self.device.set_volume(current_volume - 10)
    
        def volume_up(self):
            current_volume = self.device.get_volume()
            self.device.set_volume(current_volume + 10)
    
        def channel_down(self):
            current_channel = self.device.get_channel()
            self.device.set_channel(current_channel - 1)
    
        def channel_up(self):
            current_channel = self.device.get_channel()
            self.device.set_channel(current_channel + 1)
    
    # 精确抽象
    class AdvancedRemoteControl(RemoteControl):
        def mute(self):
            self.device.set_volume(0)
    
    # 客户端代码
    tv = Tv()
    remote = RemoteControl(tv)
    remote.toggle_power()  # TV is on
    remote.volume_up()      # Setting volume to 20
    

    上述代码展示了如何使用桥接模式来实现一个遥控器和电视的交互。遥控器(RemoteControl)作为抽象部分,依赖于电视(Tv),电视作为实现部分,实现了Device接口。通过这种方式,我们可以在不修改遥控器代码的情况下,为不同的设备(如收音机)创建新的实现类。

5. 简单练习:【设计模式专题之桥接模式】10-万能遥控器

""" 
【设计模式专题之桥接模式】10-万能遥控器
时间限制:1.000S  空间限制:256MB
题目描述
小明家有一个万能遥控器,能够支持多个品牌的电视。每个电视可以执行开机、关机和切换频道的操作,请你使用桥接模式模拟这个操作。
输入描述
第一行是一个整数 N(1 <= N <= 100),表示后面有 N 行输入。

接下来的 N 行,每行包含两个数字。第一个数字表示创建某个品牌的遥控和电视,第二个数字表示执行的操作。

其中,0 表示创建 Sony 品牌的电视,1 表示创建 TCL 品牌的遥控和电视;

2 表示开启电视、3表示关闭电视,4表示切换频道。

输出描述
对于每个操作,输出相应的执行结果。
输入示例
6
0 2
1 2
0 4
0 3
1 4
1 3
输出示例
Sony TV is ON
TCL TV is ON
Switching Sony TV channel
Sony TV is OFF
Switching TCL TV channel
TCL TV is OFF
"""

from abc import ABC, abstractmethod


class RemoteControl(ABC):
    def __init__(self, device: 'Device', *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.device = device
        
    def powerOn(self):
        raise NotImplemented
        
    def powerOff(self):
        raise NotImplemented
        
    def switchChannel(self):
        raise NotImplemented
        
class Device(ABC):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._isEnabled = False
        self._channel = ""
    
    def enable(self):
        raise NotImplemented
        
    def disable(self):
        raise NotImplemented
        
    def setChannel(self):
        raise NotImplemented
        
class SonyRemoteControl(RemoteControl):
    def powerOn(self):
        self.device.enable()
        print("Sony TV is ON")
        
    def powerOff(self):
        self.device.disable()
        print("Sony TV is OFF")
        
    def switchChannel(self):
        self.device.setChannel()
        print("Switching Sony TV channel")
        
class TCLRemoteControl(RemoteControl):
    def powerOn(self):
        self.device.enable()
        print("TCL TV is ON")
        
    def powerOff(self):
        print("TCL TV is OFF")
        
    def switchChannel(self):
        self.device.setChannel()
        print("Switching TCL TV channel")
        
class SonyTV(Device):
    def enable(self):
        if not self._isEnabled:
            self._isEnabled = True
        
    def disable(self):
        if self._isEnabled:
            self._isEnabled = False
        
    def setChannel(self):
        self._channel = "CCTV"
        
class TCLTV(Device):
    def enable(self):
        if not self._isEnabled:
            self._isEnabled = True
        
    def disable(self):
        if self._isEnabled:
            self._isEnabled = False
        
    def setChannel(self):
        self._channel = "CCTV"
        
def client():
    n = int(input())
    for _ in range(n):
        brand, option = input().split(" ")
        if brand == "0":
            rc = SonyRemoteControl(SonyTV())
        # if brand == "1":
        else:
            assert brand == "1"
            rc = TCLRemoteControl(TCLTV())
        if option == "2":
            rc.powerOn()
        if option == "3":
            rc.powerOff()
        if option == "4":
            rc.switchChannel()
            
if __name__ == "__main__":
    client()