Skip to content

Instantly share code, notes, and snippets.

@priyankvex
Created September 17, 2018 07:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save priyankvex/d594872a222fbc50b72105f4d7b3c038 to your computer and use it in GitHub Desktop.
Save priyankvex/d594872a222fbc50b72105f4d7b3c038 to your computer and use it in GitHub Desktop.
A smart home system implementation to showcase the usage of the command pattern.
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