A smart home system implementation to showcase the usage of the command pattern.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Home(object): | |
def __init__(self): | |
self._fan_state = "OFF" | |
self._fan_speed = 0 | |
def turn_fan_on(self): | |
self._fan_state = "ON" | |
def turn_fan_off(self): | |
self._fan_state = "OFF" | |
def increase_fan_speed(self, offset=1): | |
self._fan_speed = self._fan_speed + offset | |
def decrease_fan_speed(self, offset=1): | |
self._fan_speed = self._fan_speed - offset | |
def get_fan_state(self): | |
return self._fan_state | |
def get_fan_speed(self): | |
return self._fan_speed | |
class SmartRemote(object): | |
def __init__(self, home_instance): | |
# Our smart home | |
self.home = home_instance | |
# stores a log of all the actions that we have performed | |
self.command_stack = [] | |
# Decoupled commands | |
# Notice how the remote is not aware of how the commands are actually implemented. | |
# It is just responsible for invoking the right command. | |
self.fan_on_command_class = FanOnCommand | |
self.fan_off_command_class = FanOffCommand | |
self.fan_slow_command_class = FanSlowCommand | |
self.fan_fast_command_class = None | |
""" | |
Since commands are completely decoupled from the invoker, we can easily update the commands without | |
changing the invoker. | |
""" | |
def set_fan_fast_command(self, fan_fast_command): | |
self.fan_fast_command_class = fan_fast_command | |
""" | |
Invokes the fan on command and adds the command to the stack | |
""" | |
def fan_on(self): | |
if not self.fan_on_command_class: | |
raise NotImplementedError("This operation is not supported") | |
fan_on_command = self.fan_on_command_class() | |
fan_on_command.execute(self.home) | |
self.command_stack.append(fan_on_command) | |
""" | |
Invokes the fan off command and adds the command to the stack | |
""" | |
def fan_off(self): | |
if not self.fan_off_command_class: | |
raise NotImplementedError("This operation is not supported") | |
fan_off_command = self.fan_off_command_class() | |
fan_off_command.execute(self.home) | |
self.command_stack.append(fan_off_command) | |
""" | |
Invokes the fan slow command and adds the command to the stack | |
""" | |
def fan_slow(self): | |
if not self.fan_slow_command_class: | |
raise NotImplementedError("This operation is not supported") | |
fan_slow_command = self.fan_slow_command_class() | |
fan_slow_command.execute(self.home) | |
self.command_stack.append(fan_slow_command) | |
""" | |
Invokes the fan fast command and adds the command to the stack | |
""" | |
def fan_fast(self): | |
if not self.fan_fast_command_class: | |
raise NotImplementedError("This operation is not supported") | |
fan_fast_command = self.fan_fast_command_class() | |
fan_fast_command.execute(self.home) | |
self.command_stack.append(fan_fast_command) | |
""" | |
Undo the last action that was performed. | |
A major benefit of command pattern is that logging actions/commands becomes easy. | |
This can be easily extended to create something like a undo/redo feature. | |
""" | |
def undo_last_action(self): | |
last_command = self.command_stack.pop() | |
last_command.undo(home) | |
class BaseCommand(object): | |
def execute(self, home_instance): | |
raise NotImplementedError | |
def undo(self, home_instance): | |
raise NotImplementedError | |
class FanOnCommand(BaseCommand): | |
def __init__(self): | |
self._prev_fan_state = None | |
def execute(self, home_instance): | |
home_instance.turn_fan_on() | |
print ("Fan state is now {0}".format(home_instance.get_fan_state())) | |
def undo(self, home_instance): | |
home_instance.turn_fan_off() | |
print("UNDO: Fan state is now {0}".format(home_instance.get_fan_state())) | |
class FanOffCommand(BaseCommand): | |
def __init__(self): | |
self._prev_fan_state = None | |
def execute(self, home_instance): | |
home_instance.turn_fan_off() | |
print("Fan state is now {0}".format(home.get_fan_state())) | |
def undo(self, home_instance): | |
home_instance.turn_fan_on() | |
print("UNDO: Fan state is now {0}".format(home_instance.get_fan_state())) | |
class FanFastCommand(BaseCommand): | |
def __init__(self): | |
self._prev_fan_speed = None | |
def execute(self, home_instance): | |
home_instance.increase_fan_speed(offset=1) | |
print("Fan speed is now {0}".format(home_instance.get_fan_speed())) | |
def undo(self, home_instance): | |
home_instance.decrease_fan_speed(offset=1) | |
print("UNDO: Fan speed is now {0}".format(home_instance.get_fan_speed())) | |
class FanSlowCommand(BaseCommand): | |
def execute(self, home_instance): | |
home_instance.decrease_fan_speed(offset=1) | |
print("Fan speed is now {0}".format(home_instance.get_fan_speed())) | |
def undo(self, home_instance): | |
home_instance.increase_fan_speed(offset=1) | |
print("UNDO: Fan speed is now {0}".format(home_instance.get_fan_speed())) | |
if __name__ == '__main__': | |
home = Home() | |
smart_remote = SmartRemote(home) | |
# Since the commands and the invoker are decoupled, we can easily | |
# change the commands. | |
smart_remote.fan_fast_command_class = FanFastCommand | |
smart_remote.fan_on() | |
smart_remote.fan_fast() | |
smart_remote.fan_fast() | |
smart_remote.fan_slow() | |
# Notice how easy it is to undo actions. | |
# This is also useful in keeping a log of actions that have taken place. | |
smart_remote.undo_last_action() | |
smart_remote.undo_last_action() | |
# Like a responsible citizen always turn the fan off when leaving the room. | |
smart_remote.fan_off() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment