Answers:
我包括了几个GoF设计模式的封装层次表,以帮助解释这两种模式之间的差异。希望它能更好地说明每个封装的内容,以便我的解释更有意义。
首先,层次结构列出了适用于给定模式的范围,或用于封装某种详细程度的适当模式,具体取决于您从表的哪一侧开始。
从表中可以看到,策略模式对象隐藏了算法实现的详细信息,因此使用不同的策略对象将执行相同的功能,但方式不同。每个策略对象可能针对特定因素进行了优化,或者对其他参数进行了操作;并且通过使用通用接口,上下文可以安全地与任何一个一起工作。
与算法相比,命令模式封装的详细程度要低得多。它对将消息发送到对象所需的细节进行编码:接收者,选择器和参数。使过程执行的一小部分客观化的好处是,可以沿着通用的方式沿不同的时间点或位置调用此类消息,而不必对其细节进行硬编码。它允许消息被调用一次或多次,或传递到系统或多个系统的不同部分,而无需在执行之前知道特定调用的详细信息。
正如典型的设计模式一样,它们不需要所有实现都在细节上完全相同才能带有模式名称。详细信息可能会有所不同,具体实现方式以及对象中编码的数据与方法参数不同。
策略封装算法。命令将请求的发送者和接收者分开,它们将请求变成对象。
如果这是一种算法,将如何完成某事,请使用策略。如果需要将方法的调用与执行分开,请使用Command。当您将消息排队等待以后使用时(例如任务或事务),通常会使用命令。
回答一个非常老的问题。(有人看到的是最新的答案,而不是投票最多的人吗?)
由于相似之处,这确实是一个困惑。策略和命令模式都利用封装。但这并不能使它们相同。
关键的区别是要了解什么是封装。两种模式都依赖的OO原理是封装变化的东西。
在策略的情况下,变化的是算法。例如,一个策略对象知道如何输出到XML文件,而另一个策略对象则输出到JSON。不同的算法被保存(封装)在不同的类中。它是如此简单。
如果使用命令,则请求本身会有所不同。请求可能来自File Menu > Delete
或Right Click > Context Menu > Delete
或Just Delete Button pressed
。这三种情况都可以生成3个相同类型的命令对象。这些命令对象仅代表3个删除请求。不删除算法。由于请求现在是一堆对象,因此我们可以轻松地管理它们。突然,提供诸如撤消或重做的功能变得微不足道了。
命令如何实现所请求的逻辑无关紧要。在调用execute()时,它可以实现触发删除的算法,甚至可以将其委托给其他对象,甚至可以委托给策略。它只是命令模式的实现细节。这就是为什么它被命名为命令,虽然它不是一个礼貌的方式要求: - )
与策略对比;该模式仅与要执行的实际逻辑有关。如果这样做,则有助于以最少的类集实现行为的不同组合,从而防止类爆炸。
我认为,Command可以帮助我们拓宽对封装的理解,而Strategy可以自然使用封装和多态性。
我的观察方式是,您有多种方式来做同一件事,每种方式都是一种策略,而运行时的某种方式则决定执行哪种策略。
也许先尝试StrategyOne,如果结果不够好,请尝试StrategyTwo ...
命令绑定到需要发生的不同事物,例如TryToWalkAcrossTheRoomCommand。每当某些对象尝试在房间中走动时都会触发此命令,但是在其中,它可能会尝试通过StrategyOne和StrategyTwo来走过房间。
标记
我认为我可能是错的,但我将命令视为要执行的功能或响应。至少应该有两个参与者:一个请求动作的人和一个执行动作的人。GUI是命令模式的典型示例:
该命令通常在一定范围或业务范围内,但不是必需的:您可能具有execute()
在一个应用程序中实现同一接口(例如,单一方法)的发出账单,启动火箭或删除文件的命令。通常命令是自包含的,因此它们不需要执行者来处理他们要完成的任务(所有必要的信息都在构造时给出),有时命令是上下文相关的,应该能够发现此上下文(Backspace命令应知道文本中的插入符号位置以正确删除前一个字符;Rollback命令应发现要回滚的当前事务; ...)。
该战略是一个有点不同:它更势必一些区域。该策略可以定义规则以格式化日期(以UTC?特定于语言环境的语言?)(“日期格式器”策略)或计算几何图形的平方(“平方计算器”策略)。在这种意义上,策略就是轻量级对象,它们将某些东西作为输入(“日期”,“图”,...)并在其基础上做出一些决定。也许不是最好的,但是很好的策略示例是与javax.xml.transform.Source
接口相关的示例:取决于传递的对象是DOMSource
还是策略,SAXSource
或者StreamSource
策略(在这种情况下为XSLT转换器)将应用不同的规则来处理它。实现可以是简单的,也可以switch
涉及责任链模式。
但实际上,这两种模式之间存在一些共同点:命令和策略将算法封装在同一语义区域内。
命令:
基本组成:
execute()
工作流程:
客户致电祈求者 => Invoker调用ConcreteCommand => ConcreteCommand调用Receiver方法,该方法实现了抽象的Command方法。
优点:客户端不受Command和Receiver更改的影响。调用方在客户端和接收方之间提供了松散的耦合。您可以使用同一个Invoker运行多个命令。
命令模式使您可以使用同一 Invoker在不同的接收器上执行命令。调用者不知道接收方的类型
为了更好地理解概念,请查看此JournalDev 文章由潘卡·库马尔和dzone 文章由詹姆斯Sugrue除了维基百科的链接。
您可以使用命令模式
解耦命令的调用者和接收者
实现回调机制
实现撤消和重做功能
维护命令历史
java.lang.Thread
是Command模式的一种很好的实现。您可以将Thread视为调用程序,并将Runnable实现为ConcreteCommonad / Receiver和run()
方法命令。
可以在Theodore Norvell的网站上阅读命令模式的撤消/重做版本 文章中
战略:
策略模式很容易理解。在以下情况下使用此模式
您有一个算法的多个实现,并且该算法的实现可以在运行时根据特定条件进行更改。
以航空公司预订系统的票价部分为例
航空公司希望在不同的时间段(高峰和非高峰月份)提供不同的票价。在非高峰旅行期间,它希望通过提供有吸引力的折扣来刺激需求。
策略模式的关键点:
带有代码示例的相关文章:
对我而言,差异是意图之一。两种模式的实现都非常相似,但是目的不同:
对于一个策略,使用对象的组件知道什么对象呢(而且将用它来执行自己的工作的一部分),但它并不关心怎么它做它。
对于Command,使用该对象的组件既不知道 Command做什么也不知道它如何执行-它只知道如何调用它。调用者的任务只是运行命令-命令执行的处理不构成调用者核心工作的一部分。
区别是-使用组件的对象实际上是否知道或关心组件的功能?在大多数情况下,可以根据模式对象是否向其调用者返回值来确定。如果调用者关心模式对象的功能,则可能希望它返回某些内容,这将是一个策略。如果它不关心任何返回值,则可能是Command(注意,像Java Callable这样的东西仍然是Command,因为尽管返回值,但调用方并不关心该值,它只是将其传回到最初提供的命令)。