patternpythonMinor
Implementing command pattern in Python
Viewed 0 times
pythoncommandimplementingpattern
Problem
I did some study on the command pattern but most of its examples were in Java so, there must be some difference in implementation in Python. I implemented it in Python with some minor differences, please let me know if something is not correct.
O/P:
from abc import ABCMeta
from abc import abstractmethod
import inspect
import os
class Command(object):
"""
Abstract / Interface base class for commands.
"""
__metaclass__ = ABCMeta
@abstractmethod
def execute(self):
pass
@abstractmethod
def undo(self):
pass
class CreateCommand(Command):
"""
Create command implementation.
"""
def __init__(self, name):
self.file_name = name
def execute(self, name):
open(self.file_name, 'w')
print str(self) + ':::Method:::' + inspect.stack()[0][3]
def undo(self):
os.remove(self.file_name)
print str(self) + ':::Method:::' + inspect.stack()[0][3]
class MoveCommand(Command):
"""
Move command implementation.
"""
def __init__(self, src, dest):
self.src = src
self.dest = dest
def execute(self, src, dest):
os.rename(self.src, self.dest)
print str(self) + ':::Method:::' + inspect.stack()[0][3]
def undo(self):
os.rename(self.dest, self.src)
print str(self) + ':::Method:::' + inspect.stack()[0][3]
class Invoker(object):
def __init__(self, command):
self.command = command
def do(self):
self.command.execute()
def undo(self):
self.command.undo()
# Client for the command pattern
if __name__ == '__main__':
create_cmd = CreateCommand('/tmp/foo.txt')
move_cmd = MoveCommand('/tmp/foo.txt', '/tmp/bar.txt')
create_invoker = Invoker(create_cmd)
move_invoker = Invoker(move_cmd)
create_invoker.do()
move_invoker.do()
move_invoker.undo()
create_invoker.undo()O/P:
:::Method:::execute
:::Method:::execute
:::Method:::undo
:::Method:::undoSolution
The important thing to know about commands in Python (or any language with first-class functions) is that they're usually trivial. Commands with only one operation (
Comments on the above implementation:
execute) are simply functions. There's no need to define classes. Even commands with two operations can be represented as pairs of functions: (do, undo). The whole, heavyweight pattern is almost never used.Comments on the above implementation:
- Command parameters such as filenames should be arguments to the constructor, not to
execute. The code that callsexecutedoesn't know what these values should be, so they need to already be in the command.
- There's no reason to keep a table of commands. Just create commands as needed.
Invoker.executemanually dispatches by command name. Instead it should blindly callexecuteorundo, and let method dispatch find the appropriate method.
- The
Invokerclass does nothing useful.
- Why bother getting the method name from
inspect.stackwhen the method already knows its own name?
- I might call
executedo, for symmetry withundo.
ABCMetais unnecessary complexity. If you must use it, it's clearer to writeclass Command(metaclass=ABCMeta)rather than assigning to__metaclass__.
- The file operations don't undo properly if a file was overwritten. (This is not relevant to the pattern; it's just a bug.)
Context
StackExchange Code Review Q#51003, answer score: 6
Revisions (0)
No revisions yet.