0

As per figure 5 in DIP I wrote the following code. How do I write it to conform to figure 6 in the same document?

class Lamp:
    def __init__(self):
        self.state = "off"
    def turn_on(self):
        self.state = "on"
    def turn_off(self):
        self.state = "off"

class Button:
    def __init__(self, lamp):
        self.lamp = lamp
        self.state = "off"
        self.lamp.state = "off" 
    def on(self):
        self.state = "on"
        self.lamp.turn_on()
    def off(self):
        self.state = "off"
        self.lamp.turn_off()

lamp = Lamp()
button = Button(lamp)
assert lamp.state == "off"
button.on()
assert lamp.state == "on"

I am thinking of using the bridge pattern, but I am not sure how to write it. I am trying the following code but it still feels wrong to me despite the asserts running ok:

from abc import ABC, abstractmethod

class AbstractButton(ABC):
    def __init__(self, button_client):
        self.button_client = button_client
    @abstractmethod
    def on(self):
        pass
    @abstractmethod
    def off(self):
        pass

class Button(AbstractButton):
    def on(self):
        self.button_client.on()
    def off(self):
        self.button_client.off()

class ButtonClient(ABC):
    @abstractmethod
    def on(self):
        pass
    @abstractmethod
    def off(self):
        pass

class Lamp(ButtonClient):
    def __init__(self):
        self.state = "off"
    def on(self):
        self.state = "on"
    def off(self):
        self.state = "off"


lamp = Lamp()
button = Button(lamp)
assert lamp.state == "off"
button.on()
assert lamp.state == "on"
button.off()
assert lamp.state == "off"

Now inspired by Python protocols, and here I can write the following simpler code, that gets me a Button containing a Lamp Switchable device. There is only one place where the state is stored (the Lamp device) and I get controls on both for free. But is this code decoupled?

class Switchable:
    def __init__(self, device):
        self.device = device
        self.device.state = 'off'
    def turn_on(self):
        self.device.state = 'on'
    def turn_off(self):
        self.device.state = 'off'


class Lamp(Switchable):
    def __init__(self):
       super().__init__(self) 
    def __str__(self):
        return f"Lamp {self.state}"

lamp = Lamp()
button = Switchable(lamp)
assert str(lamp) == "Lamp off"
button.turn_on()
assert str(lamp) == "Lamp on"
button.turn_off()
assert str(lamp) == "Lamp off"
lamp.turn_on()
assert str(lamp) == "Lamp on"
lamp.turn_off()
assert str(lamp) == "Lamp off"
assert button.device.state == "off"
 
progmatico
  • 4,714
  • 1
  • 16
  • 27

1 Answers1

1

You don't need the complicated inheritance design that is required when programming in C++. You can just simplify your first code snippet:

class Lamp:
    def __init__(self):
        self.state = "off"
    def turn_on(self):
        self.state = "on"
    def turn_off(self):
        self.state = "off"


class Button:
    def __init__(self, lamp):
        self.lamp = lamp
    def on(self):
        self.lamp.turn_on()
    def off(self):
        self.lamp.turn_off()


lamp = Lamp()
button = Button(lamp)
assert lamp.state == "off"
button.on()
assert lamp.state == "on"

The three parts of the above can be in completely different files, with only the last part needing imports to get hold of the names Lamp and Button.

quamrana
  • 37,849
  • 12
  • 53
  • 71
  • This shows composition is much better here than inheritance. – progmatico Jul 22 '23 at 15:13
  • No, this shows that in this case, with python, inheritance is unnecessary and composition is sufficient. (Composition was always required for this kind of problem.) – quamrana Jul 22 '23 at 15:16