3
\$\begingroup\$

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.

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:

<__main__.CreateCommand object at 0xb705130c>:::Method:::execute
<__main__.MoveCommand object at 0xb705134c>:::Method:::execute
<__main__.MoveCommand object at 0xb705134c>:::Method:::undo
<__main__.CreateCommand object at 0xb705130c>:::Method:::undo
asked May 17, 2014 at 15:30
\$\endgroup\$

1 Answer 1

6
\$\begingroup\$

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 (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 calls execute doesn'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.execute manually dispatches by command name. Instead it should blindly call execute or undo, and let method dispatch find the appropriate method.
  • The Invoker class does nothing useful.
  • Why bother getting the method name from inspect.stack when the method already knows its own name?
  • I might call execute do, for symmetry with undo.
  • ABCMeta is unnecessary complexity. If you must use it, it's clearer to write class 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.)
answered May 17, 2014 at 16:20
\$\endgroup\$
4
  • \$\begingroup\$ There was a bit of confusion in my mind to give parameters while initiating command object or not, infact my first implementation was like that only, but was not sure about whether to initialize command object every time or just reuse it by giving different paramerts(like I did above)?can you please give a example for the third point? how it will find the respective command? And obviously its just an example so, I didn't handle all the error conditions. Thanks \$\endgroup\$ Commented May 18, 2014 at 3:59
  • \$\begingroup\$ The inspect is there just for debugging purpose and learning a new python hack ;), also I updated the above code snippet. So, this time I have kept is mind that single invoker per command. \$\endgroup\$ Commented May 18, 2014 at 4:23
  • 1
    \$\begingroup\$ "how it will find the respective command?": Commands should be represented as Commands, not as strings. Do something like MoveCommand('foo', 'bar').execute(), not invoke('move', 'foo', 'bar'), nor invoker.do(). The Invoker class does nothing; it should be removed. \$\endgroup\$ Commented May 18, 2014 at 4:49
  • 1
    \$\begingroup\$ @vivekpoddar One common use for this pattern is to implement undo/redo. For redo to work, the UI that manages the queue must be able to call execute without passing any information. \$\endgroup\$ Commented May 18, 2014 at 7:00

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.