8.6. Bridge

8.6.1. Rationale

  • EN: Bridge

  • PL: Most

  • Type: object

8.6.2. Use Cases

  • Nested hierarchies of classes

8.6.3. Problem

  • Every time you want to add new manufacturers remote control, you need to add two classes of ManufacturerRemoteControl and ManufacturerAdvancedRemoteControl

  • If you add new another type of remote control, such as MovieRemoteControl you now has to implement three classes

Basic Remote Control (turnOn, turnOff)
Advanced Remote Control (setChannel)
Movie Remote Control (play, pause, rewind)
RemoteControl
    SonyRemoteControl
    SamsungRemoteControl
    AdvancedRemoteControl
        SonyAdvancedRemoteControl
        SamsungAdvancedRemoteControl
../../_images/designpatterns-bridge-problem.png
from abc import ABCMeta, abstractmethod


class RemoteControl(metaclass=ABCMeta):
    @abstractmethod
    def turn_on(self) -> None:
        pass

    @abstractmethod
    def turn_off(self) -> None:
        pass


class AdvancedRemoteControl(RemoteControl, metaclass=ABCMeta):
    @abstractmethod
    def set_channel(self, number: int) -> None:
        pass

class SonyRemoteControl(RemoteControl):
    def turn_off(self) -> None:
        print('Sony turn off')

    def turn_on(self) -> None:
        print('Sony turn on')


class SonyAdvancedRemoteControl(AdvancedRemoteControl):
    def set_channel(self, number: int) -> None:
        print('Sony set channel')

    def turn_off(self) -> None:
        print('Sony turn off')

    def turn_on(self) -> None:
        print('Sony turn on')

8.6.4. Design

8.6.5. Implementation

  • Hierarchy grows in two different directions

  • We can split complex hierarchy into two hierarchies which grows independently

../../_images/designpatterns-bridge-usecase1.png
../../_images/designpatterns-bridge-usecase2.png
from abc import ABCMeta, abstractmethod
from dataclasses import dataclass


class Device(metaclass=ABCMeta):
    @abstractmethod
    def set_channel(self, number: int) -> None:
        pass

    @abstractmethod
    def turn_off(self) -> None:
        pass

    @abstractmethod
    def turn_on(self) -> None:
        pass


@dataclass
class RemoteControl:
    _device: Device

    def turn_on(self) -> None:
        self._device.turn_on()

    def turn_off(self) -> None:
        self._device.turn_off()


class AdvancedRemoteControl(RemoteControl):
    def set_channel(self, number: int) -> None:
        self._device.set_channel(number)


class SonyTV(Device):
    def set_channel(self, number: int) -> None:
        print('Sony set channel')

    def turn_off(self) -> None:
        print('Sony turn off')

    def turn_on(self) -> None:
        print('Sony turn on')


class SamsungTV(Device):
    def set_channel(self, number: int) -> None:
        print('Samsung set channel')

    def turn_off(self) -> None:
        print('Samsung turn off')

    def turn_on(self) -> None:
        print('Samsung turn on')


if __name__ == '__main__':
    remote_control = RemoteControl(SonyTV())
    remote_control.turn_on()

    remote_control = AdvancedRemoteControl(SonyTV())
    remote_control.turn_on()

    remote_control = RemoteControl(SamsungTV())
    remote_control.turn_on()

8.6.6. Assignments