251

策略设计模式和状态设计模式有什么区别?我在网上浏览了很多文章,但无法清楚地看出区别。

有人可以解释一下外行术语的区别吗?

4

21 回答 21

153

老实说,这两种模式在实践中非常相似,并且它们之间的定义差异往往取决于您询问的对象。一些流行的选择是:

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

“经典”实现将匹配列表中每个项目的状态或策略,但您确实会遇到混合了两者的混合体。一个特定的人是更具 State-y 还是 Strategy-y 最终是一个主观问题。

于 2012-10-12T23:10:10.557 回答
129
  • 策略模式实际上是关于拥有完成(基本上)相同事情的不同实现,以便一个实现可以根据策略需要替换另一个实现。例如,您可能在策略模式中有不同的排序算法。对象的调用者不会根据所采用的策略而改变,但无论策略如何,目标都是相同的(对集合进行排序)。
  • 状态模式是关于根据状态做不同的事情,同时让调用者从适应每个可能状态的负担中解脱出来。因此,例如,您可能有一个 getStatus()方法将根据对象的状态返回不同的状态,但该方法的调用者不必对每个潜在状态进行不同的编码。
于 2009-11-01T20:32:20.160 回答
94

不同之处仅在于它们解决了不同的问题:

  • 状态模式处理对象是什么(状态或类型)——它封装了依赖于状态的行为, 而
  • 策略模式处理对象如何执行特定任务——它封装了一个算法。

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


关于它们的优点的一些观察:

通过使用状态模式,状态持有(上下文)类不再需要知道它是什么状态或类型以及可用的状态或类型。这意味着该类遵循开放-封闭设计原则 (OCP):对于存在的状态/类型的更改,该类是封闭的,但状态/类型对扩展是开放的。

通过使用策略模式,算法使用(上下文)类从如何执行特定任务(--“算法”)的知识中解脱出来。这个案例也创建了对 OCP 的遵守;该课程因有关如何执行此任务的更改而关闭,但该设计对添加其他算法以解决此任务非常开放。
这也可能提高上下文类对单一责任原则 (SRP) 的遵守。此外,该算法很容易被其他类重用。

于 2012-07-16T12:44:10.957 回答
55

有人可以用外行的话解释一下吗?

设计模式并不是真正的“外行”概念,但我会尽量使其清楚。任何设计模式都可以从三个维度来考虑:

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

让我们比较一下状态和策略。

模式解决的问题

状态用于两种情况之一[GoF book p. 306]

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

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

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

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

策略用于以下[GoF book p. 316]

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

应用策略的最后一种情况与称为Replace conditional with polymorphism的重构有关。

总结:状态和战略解决了非常不同的问题。如果您的问题不能用有限状态机建模,那么状态模式可能不合适。如果您的问题不在于封装复杂算法的变体,则 Strategy 不适用。

模式的静态结构

State具有以下 UML 类结构:

状态模式PlantUML类图

Strategy具有以下 UML 类结构:

策略模式PlantUML类图

总结:就静态结构而言,这两种模式大多是相同的。事实上,像这样的模式检测工具认为“ [...] 模式的结构是相同的,禁止通过自动过程(例如,不参考概念信息)来区分它们。

但是,如果 ConcreteStates 自己决定状态转换(参见上图中的“可能决定”关联),则可能存在重大差异。这导致具体状态之间的耦合。例如(参见下一节),状态 A 决定了到状态 B 的转换。如果 Context 类决定了到下一个具体状态的转换,这些依赖关系就消失了。

模式的动力学

正如上面问题部分中提到的,状态意味着行为在运行时会根据对象的某些状态而改变。因此,状态转换的概念适用,正如与有限状态机的关系所讨论的那样。[GoF] 提到转换可以在 ConcreteState 子类中定义,也可以在集中位置(例如基于表的位置)中定义。

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

PlantUML 状态转换图,具有两种状态和一种转换

假设子类决定状态转换(通过返回下一个状态对象),动态看起来像这样:

状态转换的 PlantUML 序列图

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

策略转换的 PlantUML 序列图

摘要:每种模式都使用多态调用来根据上下文执行某些操作。在 State 模式中,多态调用(transition)经常会导致下一个state的变化。在策略模式中,多态调用通常不会改变上下文(例如,使用信用卡支付一次并不意味着您下次将使用 PayPal 支付)。同样,状态模式的动态由其相应的有限状态机决定,这(对我而言)对于正确应用该模式至关重要。

于 2015-05-24T14:18:33.180 回答
26

策略模式涉及将算法的实现从托管类中移出并将其放入单独的类中。这意味着宿主类不需要自己提供每个算法的实现,这很可能导致代码不干净。

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

状态模式涉及当对象的状态改变时改变对象的行为。这意味着宿主类没有为它可能处于的所有不同状态提供行为的实现。宿主类通常封装一个类,该类提供给定状态所需的功能,并切换到不同的类当状态改变时。

于 2009-11-01T21:20:01.890 回答
23

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

  • 工作日
  • 节假日

要处理这种情况,您可以使用State Pattern

  • 假期:IVR 只是回复说“只能在工作日的上午 9 点到下午 5 点之间接听电话”。
  • 工作日:它通过将客户连接到客户服务主管来响应。

这种将客户与支持主管联系起来的过程本身可以使用一种策略模式来实现,其中主管是根据以下任何一项来挑选的:

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

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

于 2013-04-17T11:06:29.343 回答
15

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

于 2009-11-01T20:18:47.927 回答
12

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

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

换句话说,对策略进行子类化不会改变基本算法,只会改变单个步骤。

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

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

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

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

最重要的区别是:当你选择了一个策略后,你会坚持下去,直到你完成它。这意味着您将其称为“主要方法”,只要该方法正在运行,您就永远不会更改策略。OTOH 在系统运行期间的状态模式情况下,您可以随意更改状态。

于 2011-06-01T12:28:07.897 回答
12

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

来自wiki Strategy 模式文章的 UML 图:

在此处输入图像描述

主要特征:

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

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

策略模式的真实示例

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

来自wiki状态模式文章的 UML 图 :

在此处输入图像描述

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

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

与sourcemakingjournaldev文章的主要区别:

  1. 状态战略的区别在于绑定时间。Strategy 是一种 bind-once 模式,而 State 则更加动态
  2. 国家战略之间的区别在于意图。有了 Strategy,算法的选择是相当稳定的使用 State,“上下文”对象的状态变化会导致它从 Strategy 对象的“调色板”中进行选择
  3. 上下文包含状态作为实例变量,并且可以有多个任务的实现可以依赖于状态,而在策略模式中,策略作为参数传递给方法,上下文对象没有任何变量来存储它。
于 2016-05-25T12:41:13.890 回答
10

用通俗的话来说,

在策略模式中,没有状态或所有状态都具有相同的状态。所有人都有不同的方式来完成一项任务,就像不同的医生以不同的方式对待同一病人的同一疾病一样。

在状态模式中,主观上有状态,例如患者当前的状态(例如高温或低温),将根据该状态决定下一步的行动(药物处方)。并且一种状态可以导致另一种状态,因此有状态状态依赖(技术上的组合)。

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

于 2015-10-09T18:07:35.333 回答
4

状态在状态派生类中带有一点依赖性:就像一个状态知道其他状态在它之后。例如,对于任何季节状态,Summer 在冬季之后,或者对于购物,Delivery 状态在Deposit 状态之后。

另一方面,Strategy 没有这样的依赖项。在这里,可以根据程序/产品类型初始化任何类型的状态。

于 2016-11-19T07:05:44.567 回答
3

两种模式都委托给具有多个派生类的基类,但这些派生类仅在状态模式中保留对上下文类的引用。

另一种看待它的方式是,策略模式是状态模式的更简单版本。一个子模式,如果你喜欢的话。这实际上取决于您是否希望派生状态保留对上下文的引用(即:您是否希望它们在上下文中调用方法)。

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

于 2009-11-01T20:41:34.243 回答
3

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

于 2013-08-27T11:02:41.153 回答
3

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

对于状态模式,让我们考虑一个媒体播放器播放按钮的示例。当我们开始播放时,它开始播放并让上下文知道它正在播放。每次客户端想要执行播放操作时,他都会检查播放器的当前状态。现在客户端通过上下文对象知道对象的状态正在播放,因此他调用了暂停状态对象的操作方法。客户端实现状态的部分以及它需要在什么状态下执行操作可以自动化。

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

两者都实现了开闭原则,因此开发人员能够向状态模式和新算法添加新状态。

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

于 2016-10-14T09:17:36.847 回答
2

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

一个典型的例子是你有一个优化函数的框架。框架设置数据和参数。策略模式允许您在不改变框架的情况下选择最速下降、共轭梯度、BFGS 等算法。

于 2009-11-01T20:18:50.967 回答
2

正如维基百科所说的状态模式:

状态模式是一种行为软件设计模式,它允许对象在其内部状态发生变化时改变其行为。这种模式接近于有限状态机的概念。

让我们谈谈现实世界的例子,它是汽车的方向盘。方向盘可以更换。我们可以设置更大或更小的方向盘。然而,我们并不认为小方向盘比大方向盘对汽车前轮的角度更大。

因此,我们可以得出结论,我们的汽车的行为取决于我们设置的转向工具。例如,如果我们设置较小的方向盘,我们的汽车会向左或向右转得更快。

因此,汽车会响应TurnLeft()或等事件TurnRight()。但是,汽车车轮可以转动的角度取决于当前选择的方向盘。让我们尝试编码:

public interface ISteeringWheel
{
    void TurnLeft();
    void Straight();
    void TurnRight();
}


public class BigSteeringWheel : ISteeringWheel
{
    public void Straight()
    {
      Console.WriteLine("BigSteeringWheel is straight");
    }
 
    public void TurnLeft()
    {
        Console.WriteLine("BigSteeringWheel is turned left 10  
            degrees");
    }

    public void TurnRight()
    {
        Console.WriteLine("BigSteeringWheel is turned right 10  
            degrees");
    }
}


public class SmallSteeringWheel : ISteeringWheel
{
    public void Straight()
    {
        Console.WriteLine("SmallHandleBar is straight");
    }

    public void TurnLeft()
    {
        Console.WriteLine("SmallHandleBar is turned left 
           20 degrees");
    }
    public void TurnRight()
    {
        Console.WriteLine("SmallHandleBar is turned right 20  
            degrees");
    }
}

Automobile类:

public class Automobile
{
    public ISteeringWheel SteeringWheel { get; private set; }

    public Automobile()
    {
        SteeringWheel = new BigSteeringWheel();
    }
    public void TurnLeft()
    {
         SteeringWheel.TurnLeft();
    }
    
    public void TurnRight()
    {
        SteeringWheel.TurnRight();
    }

    public void SetSteeringWheel(ISteeringWheel handleBar)
    {
        SteeringWheel = handleBar;
    }
}

策略模式:

来自维基百科的定义:

策略模式(也称为策略模式)是一种行为软件设计模式,可以在运行时选择算法。代码不是直接实现单个算法,而是接收运行时指令,以确定要使用一系列算法中的哪一个。

注意诸如«要使用的算法系列»之类的词。所以让我们假设我们有一辆真正的汽车,当司机向左转动方向盘时,我们希望我们的汽车会做以下动作:

  • 将汽车车轮向左转 10 度
  • 打开汽车左侧橙色信号

因此,上述两个动作可以被认为是«家庭算法使用»。让我们编写这个例子。

方向盘算法:

public interface ISteeringWheel
{
    void TurnLeft();
    void Straight();
    void TurnRight();
}

public class BigSteeringWheel : ISteeringWheel
{
    public void Straight()
    {
        Console.WriteLine("BigSteeringWheel is straight");
    }
 
    public void TurnLeft()
    {
        Console.WriteLine("BigSteeringWheel is turned left 
           10 degrees");
    }
    public void TurnRight()
    {
        Console.WriteLine("BigSteeringWheel is turned right 
            10 degrees");
    }
}

转向信号算法:

public interface ITurnSignal
{
    void TurnOnLeft();
    void TurnOnRight();
}

public class OrangeTurnSignal : ITurnSignal
{
    public void TurnOnLeft()
    {
        Console.WriteLine("Left OrangeTurnSignal is turned on");
    }
    public void TurnOnRight()
    {
        Console.WriteLine("Right OrangeTurnSignal is turned on");
    }
}

汽车类:

public class Automobile
{
    public ISteeringWheel SteeringWheel { get; private set; }
    public ITurnSignal TurnSignal { get; private set; }

    public Automobile()
    {
        SteeringWheel = new BigSteeringWheel();
        TurnSignal = new OrangeTurnSignal();
    }
    public void TurnLeft()
    {
        SteeringWheel.TurnLeft();
        TurnSignal.TurnOnLeft();
    }

    public void TurnRight()
    {
        SteeringWheel.TurnRight();
        TurnSignal.TurnOnRight();
    }
}

结论:

和外观非常相似State patternStrategy pattern但是,有一个微小的区别,它State pattern只有一个状态,所有的行为,如 «TurnLeft» 和 «TurnRight» 都封装在一个类中。另一方面,Strategy pattern没有单一的状态,但它有许多状态,如«SteeringWheel»和«TurnSignal»。这些不同的行为使用不同的策略对象进行封装,例如 «SteeringWheel» 和 «TurnSignal» 对象。因此,这是状态模式和策略模式之间的主要区别。

此外,我们可以将Strategy pattern视为子类化的一个很好的替代方案。继承为我们提供了类之间非常紧密的耦合,并且类之间的这种耦合是在编译时定义的。但是,Strategy pattern使用允许在运行时通过与不同对象组合来设置行为的组合。

State pattern也可以被认为是替代if — else类中许多语句的替代方法。

于 2020-10-25T19:47:15.667 回答
2

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

于 2018-04-28T18:14:55.340 回答
1

当您有一个可以分为 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 中使用不同的算法组合来拥有更多状态。状态模式允许您从一种状态切换到另一种状态并执行不同的算法组合。

于 2019-03-14T22:30:52.117 回答
1

“策略”只是一种算法,您可以根据需要在不同情况下更改它,它会为您处理一些事情。前任。您可以选择如何压缩文件。zip或rar ...在一个方法。

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

于 2019-09-12T07:02:53.547 回答
1

这两种模式都用于改变对象的行为,

按照设计,状态模式对象具有单一状态,并且对象的行为基于实现的单一状态(类)及其子类。

相反,策略没有单一的状态,一个对象的行为是由不同策略对象的实现决定的。

于 2020-12-06T03:57:30.317 回答
0
  1. 在实现搜索的策略模式中,我们可以有多种搜索策略,例如NaiveStrategy()KMPStrategy()RabinKarp()策略。这些都是独立的,并且有一些稳定的选择。最重要的是,战略不能相互转换。只有Context能够改变策略。
  2. 另一方面,状态模式基于 的概念Finite-State Machines。这些状态可以相互转换。与策略相比,这里的状态不太稳定。有一件事,每个具体状态都维护一个对另一个状态的引用context,因此能够转换到另一个状态。

所以关键是,在策略中,只有上下文可以设置策略,而在状态模式的情况下,状态可以执行到其他状态的转换。策略模式中的策略不知道彼此。虽然状态在状态模式中并非不知道彼此并允许转换,因为它们保持对上下文对象的引用。

“策略使这些对象完全独立并且彼此不知道。但是,状态并不限制具体状态之间的依赖关系,让它们随意改变上下文的状态。”

参考:https ://refactoring.guru/design-patterns/strategy

于 2022-01-10T23:56:11.637 回答