使用策略模式和命令模式


121

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

在我看来,命令模式需要所有信息才能在创建时可用,并且能够延迟其调用(也许是脚本的一部分)。

哪些决定指导使用一种模式还是另一种模式?

Answers:


94

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

首先,层次结构列出了适用于给定模式的范围,或用于封装某种详细程度的适当模式,具体取决于您从表的哪一侧开始。

设计模式封装层次表

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

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

正如典型的设计模式一样,它们不需要所有实现都在细节上完全相同才能带有模式名称。详细信息可能会有所不同,具体实现方式以及对象中编码的数据与方法参数不同。


因此,如果我有一个使用“过滤器管道”过滤结果并使用委托作为过滤器的系统(其中每个过滤器的算法都将封装在一个函数中),该系统将被视为命令模式吗?在这种情况下,我认为过滤器功能的委托人为每种过滤器在输入和输出方面必须遵守的内容提供了各种合同。
KTF 2012年

4
@KTF,不。命令模式使用具有大多数(如果不是全部)信息(例如,接收者,选择器,参数)的对象来调用对象的方法。这是一种简单化的模式,可用于其他设计模式,例如责任链,收集和您描述的管道模式。您的代表提供的“种类合同”是另一个模式,接口。
Huperniketes 2012年

50

策略封装算法。命令将请求的发送者和接收者分开,它们将请求变成对象。

如果这是一种算法,将如何完成某事,请使用策略。如果需要将方法的调用与执行分开,请使用Command。当您将消息排队等待以后使用时(例如任务或事务),通常会使用命令。


这是有道理的en.wikipedia.org/wiki/Command_Pattern客户端和调用都打成平手,但在同一时间,他们不知道对方的存在!
Kalpesh Soni,2012年

26

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

由于相似之处,这确实是一个困惑。策略和命令模式都利用封装。但这并不能使它们相同。

关键的区别是要了解什么是封装。两种模式都依赖的OO原理是封装变化的东西

在策略的情况下,变化的是算法。例如,一个策略对象知道如何输出到XML文件,而另一个策略对象则输出到JSON。不同的算法被保存(封装)在不同的类中。它是如此简单。

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

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

与策略对比;该模式仅与要执行的实际逻辑有关。如果这样做,则有助于以最少的类集实现行为的不同组合,从而防止类爆炸。

我认为,Command可以帮助我们拓宽对封装的理解,而Strategy可以自然使用封装和多态性。


15

我的观察方式是,您有多种方式来做同一件事,每种方式都是一种策略,而运行时的某种方式则决定执行哪种策略。

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

命令绑定到需要发生的不同事物,例如TryToWalkAcrossTheRoomCommand。每当某些对象尝试在房间中走动时都会触发此命令,但是在其中,它可能会尝试通过StrategyOne和StrategyTwo来走过房间。

标记


2
RE:“做同一件事的多种方式”-似乎与策略的一些常见示例相冲突。特别是那些存在执行加法,减法,乘法等实现类的类。也许这些不是很好的例子?
约书亚·戴维斯

1
@JoshuaDavis所有这些“子策略”都是一种策略的特殊情况:算术运算。它们具有公共参数(2个操作数),并产生一个值作为结果。取决于实现方式,它们几乎以不同的方式来做相同的事情(即黑匣子)。所以我在这里看不到任何冲突,但是,相反的是:很好的例子=)
jungle_mole

7

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

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

该命令通常在一定范围或业务范围内,但不是必需的:您可能具有execute()在一个应用程序中实现同一接口(例如,单一方法)的发出账单,启动火箭或删除文件的命令。通常命令是自包含的,因此它们不需要执行者来处理他们要完成的任务(所有必要的信息都在构造时给出),有时命令是上下文相关的,应该能够发现此上下文(Backspace命令应知道文本中的插入符号位置以正确删除前一个字符;Rollback命令应发现要回滚的当前事务; ...)。

战略是一个有点不同:它更势必一些区域。该策略可以定义规则以格式化日期(以UTC?特定于语言环境的语言?)(“日期格式器”策略)或计算几何图形的平方(“平方计算器”策略)。在这种意义上,策略就是轻量级对象,它们将某些东西作为输入(“日期”,“图”,...)并在其基础上做出一些决定。也许不是最好的,但是很好的策略示例是与javax.xml.transform.Source接口相关的示例:取决于传递的对象是DOMSource还是策略,SAXSource或者StreamSource策略(在这种情况下为XSLT转换器)将应用不同的规则来处理它。实现可以是简单的,也可以switch涉及责任链模式

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


1
我将命令视为回调函数或响应。至少应该有两个参与者:一个请求操作,另一个执行...-我理解您要说的话,但是我回避使用“回调”一词,因为通常这个词“回调”意味着异步调用,您无需进行异步调用即可使命令模式有用。例如:Microsoft Word。单击工具栏按钮和按下快捷键不会调用异步命令,但是我们可以理解在这种情况下如何使用命令模式
Jim G.

我同意,尽管正如Jim所说,我将进行编辑以删除对回调的引用。
JARC 2012年

谢谢,我做了一些扩展。让我知道,如果您同意/不同意。
dma_k 2012年

5

命令:

基本组成:

  1. 命令为抽象命令声明接口,例如execute()
  2. 接收方知道如何执行特定命令
  3. 调用者拥有 ConcreteCommand,必须执行
  4. 客户端创建 ConcreteCommand并分配 Receiver
  5. ConcreteCommand定义 Command Receiver之间的绑定

工作流程:

客户致电祈求者 => Invoker调用ConcreteCommand => ConcreteCommand调用Receiver方法,该方法实现了抽象的Command方法。

优点:客户端不受Command和Receiver更改的影响。调用方在客户端和接收方之间提供了松散的耦合。您可以使用同一个Invoker运行多个命令。

命令模式使您可以使用同一 Invoker在不同的接收器上执行命令。调用者不知道接收方的类型

为了更好地理解概念,请查看此JournalDev 文章潘卡·库马尔和dzone 文章詹姆斯Sugrue除了维基百科的链接。

您可以使用命令模式

  1. 解耦命令的调用者和接收者

  2. 实现回调机制

  3. 实现撤消和重做功能

  4. 维护命令历史

java.lang.ThreadCommand模式的一种很好的实现。您可以将Thread视为调用程序,并将Runnable实现为ConcreteCommonad / Receiverrun()方法命令

可以在Theodore Norvell的网站上阅读命令模式的撤消/重做版本 文章中

战略:

策略模式很容易理解。在以下情况下使用此模式

您有一个算法的多个实现,并且该算法的实现可以在运行时根据特定条件进行更改

航空公司预订系统的票价部分为例

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

策略模式的关键点:

  1. 这是一种行为模式
  2. 基于委托
  3. 它通过修改方法行为来改变对象的胆量
  4. 用于在一系列算法之间切换
  5. 它在运行时更改对象的行为

带有代码示例的相关文章:

使用命令设计模式

战略模式的真实例子


0

对我而言,差异是意图之一。两种模式的实现都非常相似,但是目的不同:

  • 对于一个策略,使用对象的组件知道什么对象呢(而且将用它来执行自己的工作的一部分),但它并不关心怎么它做它。

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

区别是-使用组件的对象实际上是否知道或关心组件的功能?在大多数情况下,可以根据模式对象是否向其调用者返回值来确定。如果调用者关心模式对象的功能,则可能希望它返回某些内容,这将是一个策略。如果它不关心任何返回值,则可能是Command(注意,像Java Callable这样的东西仍然是Command,因为尽管返回值,但调用方并不关心该值,它只是将其传回到最初提供的命令)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.