策略设计模式和状态设计模式有什么区别?


219

策略设计模式和状态设计模式之间有什么区别?我正在浏览网上的许多文章,但无法清楚地看出两者之间的区别。

有人可以解释外行用语的区别吗?


根据这里的答案和我自己的观察,似乎实现在很大程度上(尽管不完全)相同。相反,差异主要是意图之一:我们正在尝试根据状态(状态模式)或其他情况(策略模式)来适应行为。通常情况下,通过注入,其他东西是“客户的选择”。
Timo

Answers:


139

坦白说,这两种模式在实践中非常相似,并且两者之间的定义差异往往会因您要求的人而异。一些受欢迎的选择是:

  • 状态存储对包含它们的上下文对象的引用。策略没有。
  • 允许状态替换自己(即:将上下文对象的状态更改为其他状态),而不允许使用策略。
  • 策略作为参数传递给上下文对象,而状态由上下文对象本身创建。
  • 策略仅处理单个特定任务,而国家则为上下文对象所做的所有(或大部分)提供基础实现。

“经典”实现将为列表中的每个项目匹配“状态”或“策略”,但是您会在混合了两者的混合对象上运行。一个特定的国家到底是国家还是战略,这最终是一个主观的问题。


6
如果您将GoF视为最受欢迎的选择之一,那么他们会不同意国家一定是由上下文创建的(可以由客户创建并传递到上下文中,就像使用策略一样)。
Will Hardwick-Smith

109
  • 战略模式实际上是关于具有不同的实现,完成(基本上)同样的事情,这样的策略需要一个实现可以取代其他。例如,您可能在策略模式中使用不同的排序算法。对象的调用者不会根据所采用的策略而改变,但是无论策略如何,目标都是相同的(对集合进行排序)。
  • 模式是这样做基于状态不同的事情,而留下容纳从每一个可能的状态的负担减轻调用者。因此,例如,您可能有一个getStatus()方法,该 方法将根据对象的状态返回不同的状态,但是方法的调用者不必对每个潜在的状态进行不同的编码。

1
但是谁会改变策略模式中的策略?
2013年

1
@Noor,通常是某种参数或字段。实际调用者的代码不会根据策略的更改而更改。
Yishai 2013年

4
@Noor,是的,但是在我现在可以想到的任何策略模式中,这都是一个预先决定,不会在中间改变。
Yishai 2013年

2
我遇到了同样的问题,状态或策略,我认为几句话的区别是,状态,行为是自动确定的,策略,行为是由调用者确定的。
Rene MF

1
在电子商务应用程序中,如果需要在节日期间享受额外折扣,则这是状态设计模式。如果有多种方法可以得出该数字,则可以将实际折现率逻辑应用于策略设计模式。
Bharathkumar V

85

区别仅在于它们解决了不同的问题:

  • 与模式的交易是什么(州或类型)的对象是(上) -它封装了状态相关的行为,而
  • 策略与模式的交易如何对象执行特定的任务-它封装了一个算法。

然而,实现这些不同目标的结构非常相似。两种模式都是通过委托进行组合的示例。


关于其优势的一些观察:

通过使用状态模式的国有控股(上下文)类是从知识松了一口气什么状态或键入这是什么国家或可用的类型。这意味着该类遵循开放式设计原则(OCP):该类因存在的状态/类型的更改而关闭,但是状态/类型对扩展开放。

通过使用策略模式,无需使用算法的(上下文)类就可以了解如何执行特定任务(“算法”)。这种情况也使OCP得以遵守;该类因执行此任务的方式的更改而关闭,但是该设计对添加其他用于解决此任务的算法非常开放。
这也可能会提高上下文类对单一职责原则(SRP)的遵守。此外,该算法变得易于用于其他类重用。


42

有人可以用外行的术语解释吗?

设计模式并不是真正的“外行”概念,但是我会尽力使它尽可能清晰。任何设计模式都可以从三个方面考虑:

  1. 模式解决的问题;
  2. 模式的静态结构(类图);
  3. 模式的动态(序列图)。

让我们比较状态和策略。

模式解决的问题

在以下两种情况之一中使用状态[GoF书,第9页。306]

  • 对象的行为取决于其状态,并且它必须在运行时根据该状态更改其行为。
  • 操作具有取决于对象状态的大型,多部分条件语句。此状态通常由一个或多个枚举常量表示。通常,几个操作将包含相同的条件结构。State模式将条件的每个分支放在单独的类中。这样一来,您就可以将对象的状态视为独立的对象,该对象可以独立于其他对象而变化。

如果要确定确实存在状态模式所解决的问题,则应该能够使用有限状态机对对象的状态进行建模。您可以在此处找到一个应用示例。

每个状态转换都是State接口中的一种方法。这意味着对于设计,在应用此模式之前必须非常确定状态转换。否则,如果您添加或删除转换,则将需要更改接口和实现该接口的所有类。

我个人还没有发现这种模式有用。您始终可以使用查找表来实现有限状态机(这不是OO方式,但效果很好)。

策略用于以下[GoF书,第 316]

  • 许多相关的类仅在行为上有所不同。策略提供了一种使用多种行为之一配置类的方法。
  • 您需要算法的不同变体。例如,您可以定义反映不同时空权衡的算法。当这些变体作为算法的类层次结构实现时,可以使用策略[HO87]。
  • 一种算法使用客户端不应该知道的数据。使用策略模式可避免暴露复杂的,特定于算法的数据结构。
  • 一个类定义了许多行为,这些行为在其操作中显示为多个条件语句。代替许多条件,将相关的条件分支移到其自己的Strategy类中。

最后一种应用策略的情况与重构有关,称为“ 用多态替换条件”

简介:国家和战略解决了截然不同的问题。如果您的问题无法用有限状态机建模,则可能的状态模式不合适。如果您的问题不是关于封装复杂算法的变体,那么策略就不适用。

模式的静态结构

State具有以下UML类结构:

状态模式的PlantUML类图

策略具有以下UML类结构:

策略模式的PlantUML类图

简介:就静态结构而言,这两种模式基本相同。实际上,图案检测工具,如这一个认为“ 的[...]的图案是相同的,由一个自动的过程禁止其区别(例如,不参照概念性信息)。结构

但是,如果ConcreteStates自行决定状态转换,则可能会有很大的不同(请参见上图中的“ 可能确定 ”关联)。这导致具体状态之间的耦合。例如(请参阅下一节),状态A决定了向状态B的转换。如果Context类决定了向下一个具体状态的转换,则这些依赖项将消失。

模式的动态

如上面“问题”部分所述,状态表示行为在运行时根据对象的某些状态而变化。因此,如关于有限状态机的关系所讨论的,状态转移的概念适用。[GoF]提到过渡可以在ConcreteState子类中定义,也可以在集中位置(例如基于表的位置)中定义。

让我们假设一个简单的有限状态机:

具有两个状态和一个转换的PlantUML状态转换图

假设子类决定状态转换(通过返回下一个状态对象),则动态如下所示:

用于状态转换的PlantUML序列图

为了展示策略的动态,借用一个真实的例子很有用。

用于策略过渡的PlantUML序列图

简介:每个模式都根据上下文使用多态调用来执行某些操作。在状态模式中,多态调用(转换)通常会导致下一个状态发生变化。在策略模式中,多态调用通常不会更改上下文(例如,一次使用信用卡付款并不意味着您下次将使用PayPal付款)。同样,状态模式的动态性由其相应的fininte状态机确定(对我而言)这对于正确应用此模式至关重要。


这个答案对我来说非常有用,可以让我区分出区别。状态机的参数听起来像是恕我直言。这实际上是以理论计算机科学的方式总结了以上答案。
medunes '18

这个答案非常有帮助,对我来说这是最好的。
Chofoteddy

25

策略模式包括将算法的实现从宿主类中移出并将其放在单独的类中。这意味着主机类不需要提供每个算法本身的实现,这很可能导致代码不干净。

通常将排序算法用作示例,因为它们都执行相同的事情(排序)。如果将每种不同的排序算法放在自己的类中,则客户端可以轻松选择要使用的算法,并且该模式提供了一种简便的访问方法。

状态模式涉及在对象状态改变时改变对象的行为。这意味着主机类无法为其可能处于的所有不同状态提供行为实现。主机类通常封装一个提供给定状态所需功能的类,然后切换到其他类状态改变时。


16

考虑一个处理客户呼叫的IVR(交互式语音响应)系统。您可能需要对其进行编程以处理以下方面的客户:

  • 工作日
  • 假期

要处理这种情况,可以使用状态模式

  • 假日:IVR只是回答说“ 只能在上午9点至下午5点之间的工作日拨打电话 ”。
  • 工作日:它通过将客户与客户服务主管联系来进行响应。

将客户连接到支持主管的过程本身可以使用策略模式来实现,在该策略模式中,可以根据以下任一项来选择主管:

  • 循环赛
  • 最近最少使用
  • 其他基于优先级的算法

策略模式决定“ 如何 ”执行某些动作,状态模式决定“ 何时 ”执行这些动作。


这是一个很好的答案,而且被低估了。但是,提及您的示例中为什么需要许多算法会很有帮助。例如,基于呼叫中心公司的偏好来选择算法。对于那些不了解RR或LRU的人,如果列表中有更简单的算法或琐碎的算法,这也将有所帮助。例如-长期客户获得较高优先级,等待最多的客户获得较高优先级。谢谢 !
MasterJoe2

14

策略代表“做”某事的对象,具有相同的开始和结束结果,但在内部使用不同的方法。从这个意义上讲,它们类似于表示动词的实现。状态模式OTOH使用“是”某种东西的对象-操作状态。尽管它们也可以表示对数据的操作,但它们更类似于名词的表示而不是动词的表示,并且是针对状态机量身定制的。


11

策略:策略是固定的,通常包括几个步骤。(排序仅构成一个步骤,因此是一个非常糟糕的示例,因为它太原始了,无法理解此模式的目的)。该策略中的“主要”例程正在调用一些抽象方法。例如,“进入房间策略”,“主方法”是goThroughDoor(),它看起来像:approachDoor(),如果(locked())openLock(); 开门(); enterRoom(); 转(); 关门(); 如果(wasLocked())lockDoor();

现在,用于通过可能的锁着的门从一个房间移动到另一个房间的这种通用“算法”的子类可以实现算法的步骤。

换句话说,策略的子类别不会更改基本算法,而只会更改单个步骤。

上面是模板方法模式。现在,将属于一起的步骤(解锁/锁定和打开/关闭)放入其自己的实现对象中,并委托给它们。例如,带钥匙的锁和带代码卡的锁是两种锁。将策略委托给“ Step”对象。现在您有了一个策略模式。

状态模式是完全不同的东西。

您有一个包装对象和包装的对象。包装的是“状态”。状态对象只能通过其包装器访问。现在,您可以随时更改包装的对象,因此包装程序似乎可以更改其状态,甚至可以更改其“类”或类型。

例如,您有登录服务。它接受用户名和密码。它只有一种方法:Logon(String userName,String passwdHash)。与其自行决定是否接受登录,不如将决定委托给一个状态对象。该状态对象通常仅检查用户/密码组合是否有效并执行登录。但是现在您可以通过仅允许特权用户登录(例如在维护时间)或不允许任何人登录的一种来交换“检查器”。这意味着“检查器”表示系统的“登录状态”。

最重要的区别是:选择策略后,请坚持使用它,直到完成为止。这意味着您将其称为“主要方法”,并且只要该方法正在运行,您就永远不会更改该策略。在系统运行时处于状态模式情况下的OTOH,您可以根据需要任意更改状态。


9

当您对特定任务有多种算法,并且客户端决定要在运行时使用的实际实现时,将使用策略模式。

来自Wiki策略模式文章的UML图:

在此处输入图片说明

主要特点:

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

有关更多信息和真实示例,请参阅此帖子:

战略模式的真实例子

状态模式允许对象在内部状态改变时改变其行为

Wiki状态模式文章中的UML图 :

在此处输入图片说明

如果必须根据对象的状态更改其行为,则可以在对象中使用状态变量,并使用if-else条件块根据状态执行不同的操作。状态模式用于通过上下文状态实现提供一种系统的,耦合耦合的方式来实现这一目标。

有关更多详细信息,请参阅此journaldev文章。

与源制作journaldev文章的主要区别:

  1. 国家战略之间的区别在于约束时间。该策略是一次绑定模式,而国家则更具活力
  2. 国家战略之间的区别在于意图。使用Strategy,算法的选择相当稳定使用状态,“上下文”对象状态的变化会导致它从其“调色板”策略对象中进行选择
  3. 上下文包含状态作为实例变量,并且可以有多个任务,其实现可以依赖于状态,而在策略模式中,策略作为参数传递给方法,上下文对象没有任何变量来存储它。

5

用外行的语言,

在策略模式中,没有状态或所有状态都相同。每个人都有不同的执行任务的方式,例如不同的医生以不同的方式对待处于相同状态的同一患者的相同疾病。

在状态模式中,主观上存在诸如患者当前状态(例如高温或低温)之类的状态,基于该状态将决定下一步的行动方针(药物处方),并且一个状态可以导致其他状态,因此存在状态陈述依赖关系(从技术上讲构成)。

如果我们从技术上试图理解它,则基于两者的代码比较,可能会失去情况的主观性,因为两者看起来非常相似。


2

这两种模式都委派给具有多个派生类的基类,但是只有在State模式下,这些派生类才拥有对上下文类的引用。

另一种看待它的方式是“策略”模式是“状态”模式的简单版本;子模式(如果您愿意)。这实际上取决于您是否希望派生状态将引用保留回上下文(即:是否希望它们在上下文上调用方法)。

有关更多信息:Robert C Martin(和Micah Martin)在他们的书“ C#中的敏捷原理,模式和实践”中回答了此问题。(http://www.amazon.com/Agile-Principles-Patterns-Practices-C/dp/0131857258


2

这是一个很老的问题,但是我仍然在寻找相同的答案,这就是我所发现的。

对于状态模式,请考虑Medial Player Play按钮的示例。当我们玩游戏时,它开始播放,并使上下文知道它正在播放。每当客户想要执行播放操作时,他都会检查播放器的当前状态。现在,客户端通过上下文对象知道对象的状态正在播放,因此他调用了暂停状态对象actions方法。客户端实现状态以及需要采取何种操作状态的部分可以实现自动化。

https://www.youtube.com/watch?v=e45RMc76884 https://www.tutorialspoint.com/design_pattern/state_pattern.htm

对于策略模式,类图的排列与状态模式相同。客户来此安排进行一些操作。也就是说,代替不同的状态,有不同的算法,例如需要对模式执行不同的分析。在这里,客户告诉上下文它要执行哪种算法(业务定义的自定义算法),然后执行该算法。

https://www.tutorialspoint.com/design_pattern/strategy_pattern.htm

两者都实现了开闭原理,因此开发人员具有将新状态添加到状态模式和新算法的能力。

但是不同之处在于它们使用的是状态模式,该状态模式用于根据对象的状态执行不同的逻辑。在策略的情况下,逻辑也不同。


2

状态在状态派生类中带有一些依赖性:就像一个状态知道其后的其他状态一样。例如,对于任何季节状态,夏季都在冬季之后,或者在购物的存款状态之后为交货状态。

另一方面,策略没有这样的依赖关系。在此,可以根据程序/产品类型初始化任何一种状态。


1

差异在http://c2.com/cgi/wiki?StrategyPattern中进行了讨论。我已经使用了策略模式来允许在用于分析数据的整个框架中选择不同的算法。通过它,您可以添加算法,而不必更改整体框架及其逻辑。

一个典型的例子是您拥有优化功能的框架。该框架设置数据和参数。该策略模式允许您选择算法,例如最直接下降,共轭梯度,BFGS等,而无需更改框架。


1

战略和国家模式具有相同的结构。如果您查看两种模式的UML类图,它们看起来完全相同,但是它们的意图完全不同。状态设计模式用于定义和管理对象的状态,而策略模式用于定义一组可互换的算法,并允许客户选择其中的一种。因此,策略模式是客户端驱动的模式,而对象可以自己管理状态。


1

简而言之,使用策略模式,我们可以动态设置一些行为,使用状态模式,我们可以确定,对象会随着状态的改变而在内部改变其行为。


0

当您有一个项目时,该项目可以分为2个任务:

任务1:您可以使用两种不同的算法之一来完成:alg1,alg2

任务2:您可以使用三种不同的算法之一来完成:alg3,alg4,alg5

alg1和alg2可互换;alg3,alg4和alg5可互换。

在任务1和任务2中选择执行哪种算法取决于状态:

状态1:在任务1中需要alg1,在任务2中需要alg3

状态2:在任务1中需要alg2,在任务2中需要alg5

您可以将状态对象从状态1更改为状态2。然后您的任务将由alg2和alg5代替alg1和alg3来完成。

您可以为任务1或任务2添加更多可互换的算法。这是策略模式。

您可以在任务1和任务2中使用不同的算法组合来具有更多状态,状态模式使您可以从一种状态切换到另一种状态并执行不同的算法组合。


0

“策略”只是一种算法,您可以根据需要在不同的情况下对其进行更改,并为您处理一些事情。例如 您可以选择如何压缩文件。zip或rar ...的一种方法。

但是,“状态”可以更改您的所有对象行为,当对象更改时,甚至可以更改其他字段……这就是为什么它引用其所有者的原因。您应该注意,更改对象字段完全可以更改对象行为。例如 当您在obj中将State0更改为State1时,会将整数更改为10.。因此,当我们调用obj.f0()进行一些计算并使用该整数时,它将影响结果。

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.