Python 中的通用命令模式和命令调度模式

Posted

技术标签:

【中文标题】Python 中的通用命令模式和命令调度模式【英文标题】:General Command pattern and Command Dispatch pattern in Python 【发布时间】:2010-12-02 10:34:22 【问题描述】:

我正在寻找 Python 中的 Command 模式实现... (根据Wikipedia,

命令模式是一种设计 对象用于的模式 代表和封装所有 调用方法所需的信息 稍后。

)

我发现的唯一东西是命令调度pattern:

class Dispatcher:

    def do_get(self): ...

    def do_put(self): ...

    def error(self): ...

    def dispatch(self, command):
        mname = 'do_' + command
        if hasattr(self, mname):
            method = getattr(self, mname)
            method()
        else:
            self.error()

可能是我错了,但看起来这是两个不同的概念,意外地有相似的名称。

我错过了什么吗?

【问题讨论】:

【参考方案1】:

最简单的命令模式已经内置在 Python 中,只需使用可调用对象:

def greet(who):
    print "Hello %s" % who

greet_command = lambda: greet("World")
# pass the callable around, and invoke it later
greet_command()

如果您的命令需要能够做的不仅仅是被调用,那么作为面向对象设计模式的命令模式会更有意义。常见的用例是当您需要能够撤消/重做您的操作时。那么命令类是将前进和后退动作耦合在一起的好方法。例如:

class MoveFileCommand(object):
    def __init__(self, src, dest):
        self.src = src
        self.dest = dest
        os.rename(self.src, self.dest)
    def undo(self):
        os.rename(self.dest, self.src)

undo_stack = []
undo_stack.append(MoveFileCommand('foo.txt', 'bar.txt'))
undo_stack.append(MoveFileCommand('bar.txt', 'baz.txt'))
# foo.txt is now renamed to baz.txt
undo_stack.pop().undo() # Now it's bar.txt
undo_stack.pop().undo() # and back to foo.txt

【讨论】:

感谢您的撤消示例:它很小(很容易理解)并且很好地说明了这个概念。【参考方案2】:

是的,您确实错过了一些东西:命令模式仅在没有函数指针(或作为第一类对象的函数)的语言中是必需的,例如 Java。在具有函数即对象的语言中,您可以使用函数本身;不需要有一个单独的命令对象(它应该有一个“doit”方法)。

在您能否引用的示例中,getattr() 调用为您提供“命令对象”(即绑定方法);在它“调用”(即调用)命令对象之后添加括号。

【讨论】:

我认为仅仅因为函数是第一类对象就假定命令模式不是“必要的”是一个坏主意。命令模式不仅仅是传递可调用对象,它是关于创建执行模型的健壮描述。能够将部分应用的命令保留一段时间,然后调用该命令并稍后完成它通常也很有用。在 python 中持久化函数很棘手,持久化用户定义的命令就不那么简单了。【参考方案3】:

做了一些搜索并找到了这个。它似乎完成了封装动作的工作。

def demo(a,b,c):
    print 'a:',a
    print 'b:',b
    print 'c:',c

class Command:
    def __init__(self, cmd, *args):
        self._cmd=cmd
        self._args=args

    def __call__(self, *args):
       return apply(self._cmd, self._args+args)


cmd=Command(dir,__builtins__)
print cmd()

cmd=Command(demo,1,2)
cmd(3)

【讨论】:

apply 被认为是“非必要的”:docs.python.org/2/library/…【参考方案4】:

如果我没记错的话,命令模式是关于像“文件 - 保存”这样的命令,而不是像“svn commit”这样的命令,这是你的代码的优点。

Martin 建议不需要 Command 模式,因为作为一等对象的函数取而代之,但 Command 模式比 doit() 更丰富,例如还有 undo()is_enabled() 等。

【讨论】:

我相信 undo 的集成是令人困惑的问题。我看过的命令模式的每个描述都提到您可以撤消,但实际代码只有一个 Execute() 方法的接口(不支持撤消)。所以我认为命令模式的感知“丰富性”实际上混合了独立的用例,其中主要用例只是关于无参数回调操作。

以上是关于Python 中的通用命令模式和命令调度模式的主要内容,如果未能解决你的问题,请参考以下文章

vim的简单用法和简单命令

对消息总线/命令调度程序模式的困惑

python学习第二天:命令行模式和Python交互模式

Python 设计模式 — 行为型模式 — 命令模式

vi命令

命令模式