使用策略模式和命令模式

时间:2010-10-07 16:22:45

标签: design-patterns encapsulation strategy-pattern command-pattern

两种设计模式都封装了算法,并将实现细节与其调用类分离。我能辨别的唯一区别是策略模式接受执行参数,而命令模式则没有。

在我看来,命令模式要求所有执行信息在创建时都可用,并且它能够延迟其调用(可能作为脚本的一部分)。

什么决定指导是使用一种模式还是另一种模式?

7 个答案:

答案 0 :(得分:91)

我包含了几个GoF设计模式的封装层次表,以帮助解释这两种模式之间的差异。希望它能更好地说明每个封装的内容,因此我的解释更有意义。

首先,层次结构列出了给定模式适用的范围,或者用于封装某些细节级别的适当模式,具体取决于您从哪个表开始。

design pattern encapsulation hierarchy table

从表中可以看出,策略模式对象隐藏了算法实现的细节,因此使用不同的策略对象将以不同的方式执行相同的功能。每个策略对象可以针对特定因素进行优化,或者针对其他一些参数进行操作;并且,通过使用公共接口,上下文可以安全地使用。

命令模式封装了比算法小得多的细节。它编码将消息发送到对象所需的详细信息:接收器,选择器和参数。客观化执行流程执行的这么一小部分的好处是,可以以一般方式在不同时间点或位置调用此类消息,而无需对其详细信息进行硬编码。它允许一次或多次调用消息,或者传递给系统的不同部分或多个系统,而不需要在执行之前知道特定调用的细节。

与设计模式的典型情况一样,它们并不要求所有实现都具有相同的细节以承载模式名称。细节可以在实现中以及在对象中编码的数据与方法参数之间有所不同。

答案 1 :(得分:48)

策略封装算法。命令将发送方与请求的接收方分开,它们将请求转换为对象。

如果是算法,将如何完成某项任务,请使用策略。如果需要将方法的调用与其执行分开,请使用命令。排队消息供以后使用时,通常会使用命令,如任务或事务。

答案 2 :(得分:24)

回答一个非常古老的问题。 (有人看到最新的答案,而不是大多数人投票?)

由于相似性,这是一个有效的混淆。策略和命令模式都使用封装。但这并不能使它们相同。

关键区别在于理解封装了什么。两种模式依赖的OO原则是封装变化的内容

在策略的情况下,不同的是算法。例如,一个策略对象知道如何输出到XML文件,而另一个策略对象输出到JSON。在不同的类中保留不同的算法(封装)。就这么简单。

如果是命令,请求本身会有所不同。请求可能来自File Menu > DeleteRight Click > Context Menu > DeleteJust Delete Button pressed。这三种情况都可以生成3个相同类型的命令对象。这些命令对象仅代表3个删除请求;不删除算法。由于请求现在是一堆对象,我们可以轻松地管理它们。突然,提供撤消或重做等功能变得微不足道。

命令如何实现所请求的逻辑并不重要。在调用execute()时,它可以实现一个触发删除的算法,或者甚至可以将它委托给其他对象,甚至可以委托给一个策略。它只是命令模式的实现细节。这就是为什么它被命名为命令,虽然它不是请求的礼貌方式: - )

将其与策略进行对比;此模式仅涉及执行的实际逻辑。如果我们这样做,它有助于用最少的类集实现不同的行为组合,从而防止类爆炸。

我认为,Command帮助我们拓宽了对封装的理解,同时策略提供了封装和多态的自然使用。

答案 3 :(得分:15)

我看待它的方式是你有多种方法可以做同样的事情,每种方法都是一种策略,而运行时的某些方法决定了哪种策略被执行。

也许首先尝试使用StrategyOne,如果结果不够好,请尝试使用StrategyTwo ......

命令绑定到需要发生的不同事情,如TryToWalkAcrossTheRoomCommand。只要某个对象试图穿过房间,就会触发此命令,但在其中,它可能会尝试使用StrategyOne和StrategyTwo来试图穿过房间。

标记

答案 4 :(得分:7)

我认为我可能错了,但我将command视为执行功能或反应。应该至少有两个参与者:请求操作的人和执行操作的人。 GUI是命令模式的典型示例:

  • 应用程序工具栏上的所有按钮都与某些操作相关联。
  • 按钮是这种情况下的执行者。
  • 在这种情况下,操作是命令。

该命令通常限制在某个范围或业务范围内,但不是必需的:您可能拥有发出帐单,启动火箭或删除实现相同接口的文件(例如单execute()方法)的命令一个申请。通常命令是自包含的,因此它们不需要执行程序中的任何内容来处理它们打算执行的任务(所有必要的信息都是在构造时给出的),有时命令是上下文敏感的并且应该能够发现这种上下文( Backspace 命令应该知道文本中的插入符位置以正确删除前一个字符; Rollback 命令应该发现要回滚的当前事务; ...)。

strategy有点不同:它更局限于某个区域。策略可以定义规则来格式化日期(以UTC?区域设置特定?)(“日期格式化程序”策略)或计算几何图形的平方(“方形计算器”策略)。从这个意义上说,策略是轻量级对象,它将某些东西作为输入(“日期”,“数字”,......)并在其基础上做出一些决定。也许不是最好的,但战略的好例子是与javax.xml.transform.Source接口相关的:取决于传递的对象是DOMSource还是SAXSource还是StreamSource策略(= XSLT这种情况下的变压器)将应用不同的规则来处理它。实施可以是简单的switch或涉及Chain of responsibility pattern

但实际上这两种模式之间存在一些共同点:命令和策略将算法封装在同一语义区域内。

答案 5 :(得分:4)

<强> 命令:

基本组件:

  1. 命令 execute()
  2. 等抽象命令声明一个接口
  3. Receiver 知道如何执行特定命令
  4. Invoker 持有 ConcreteCommand ,必须执行
  5. 客户 创建 ConcreteCommand 并指定 Receiver
  6. ConcreteCommand 定义 Command Receiver 之间的绑定
  7. 工作流:

    客户致电推荐人 =&gt; 邀请者致电 ConcreteCommand =&gt; ConcreteCommand 调用 Receiver 方法,该方法实现了抽象命令方法。

    优势:客户端不受命令和接收器的影响。 Invoker在客户端和接收者之间提供松耦合。您可以使用相同的Invoker运行多个命令。

    命令 模式允许您使用相同的 Invoker 在不同的 Receiver 上执行命令。 Invoker不知道 Receiver

    的类型

    为了更好地理解概念,除了 Pankaj Kumar James Sugrue 之外,请查看此JournalDev article以及dzone article维基百科链接。

    您可以使用 命令 模式

    1. 解密调用者&amp;命令接收者

    2. 实施回调机制

    3. 实施撤消和重做功能

    4. 维护命令历史

    5. java.lang.Thread Command 模式的一个很好的实现。您可以将线程视为调用者&amp;将 Runnable 作为 ConcreteCommonad / Receiver run()方法实现为 Command

      可以在 Theodore Norvell的 article

      中读取撤消/重做版本的命令模式

      <强> 策略:

      战略模式很容易理解。

      时使用此模式

      您有一个算法的多个实现,算法的实现可以在运行时根据特定条件而改变

      航空公司预订系统的票价组件

      为例

      航空公司希望在不同时段提供不同的票价 - 峰值和非峰值月份。在非高峰旅行日期间,它希望通过提供有吸引力的折扣刺激需求。

      策略 模式的关键要点:

      1. 这是一种行为模式
      2. 它基于委派
      3. 通过修改方法行为来改变对象的内容
      4. 它曾用于在算法族之间切换
      5. 它在运行时更改对象的行为
      6. 带代码示例的相关帖子:

        Using Command Design pattern

        Real World Example of the Strategy Pattern

答案 6 :(得分:0)

对我来说,区别在于意图。两种模式的实现非常相似,但有不同的用途:

  • 对于策略,使用该对象的组件知道对象的内容(并将使用它来执行自己的部分工作),但它并不关心如何它做到了。

  • 对于Command,使用该对象的组件既不知道Command执行的 ,也不知道如何 - 它只知道如何调用它。调用者的任务就是运行命令 - 命令执行的处理不构成调用者核心工作的一部分。

这就是区别 - 使用组件的对象是否真正知道或关心组件的作用?大多数情况下,这可以根据模式对象是否向其调用者返回值来确定。如果调用者关心模式对象的作用,那么它可能希望它返回一些东西,它将是一个策略。如果它不关心任何返回值,它可能是一个Command(注意,像Java Callable这样的东西仍然是一个Command,因为虽然它返回一个值,但调用者并不关心这个值 - 它只是将它传递给最初提供给命令的任何东西。