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"
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 import
s to get hold of the names Lamp
and Button
.