战略设计模式和州设计模式有什么区别?

时间:2009-11-01 20:11:09

标签: design-patterns state strategy-pattern

策略设计模式与州设计模式有何不同?我在网上经历了不少文章,但无法清楚地看出差异。

有人可以解释外行人的条款差异吗?

20 个答案:

答案 0 :(得分:120)

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

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

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

答案 1 :(得分:90)

  • 策略模式实际上是有不同的 完成(基本上)同样的事情的实现,以便 一个实现可以替代战略需要的另一个。 例如,您可能在a中有不同的排序算法 战略模式。对象的调用者不会根据更改 正在采用哪种策略,但无论策略如何 是相同的(对集合进行排序)。
  • 状态模式是基于的做不同的事情 状态,同时让呼叫者免除负担 适应每一个可能的状态。例如,你可能有一个 getStatus()方法将返回基于的不同状态 对象的状态,但方法的调用者不一定是 编码不同以考虑每个潜在的状态。

答案 2 :(得分:78)

差异在于他们解决了不同的问题:

  • State 模式处理对象所在的 what (状态或类型) - 它封装了与状态相关的行为,而
  • 策略模式处理如何对象执行某项任务 - 它封装了算法。

实现这些不同目标的结构非常相似;这两种模式都是具有授权的组合的例子。


关于其优势的一些观察:

通过使用 State 模式,状态保持(上下文)类可以从什么状态或类型以及可用状态或类型的知识中解脱出来。这意味着该类遵循开放式封闭式设计原则(OCP):该类因状态/类型的变化而关闭,但状态/类型对扩展是开放的。

通过使用策略模式,算法使用(上下文)类从如何执行某项任务( - “算法”)的知识中解脱出来。这种情况也会导致对OCP的遵守;关于如何执行此任务的更改,该类已关闭,但该设计对于添加其他算法以解决此任务非常开放。
这也可能改善上下文类对单一责任原则(SRP)的遵守。此外,该算法很容易被其他类重用。

答案 3 :(得分:37)

  

有人可以用非专业人士的说法解释一下吗?

设计模式不是真正的"外行"概念,但我会尽量让它变得清晰。任何设计模式都可以考虑三个方面:

  1. 模式解决的问题;
  2. 模式的静态结构(类图);
  3. 模式的动态(序列图)。
  4. 让我们比较状态和策略。

    模式解决问题

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

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

    如果要确保状态模式确实存在问题,则应该能够使用有限状态机对对象的状态进行建模。您可以找到应用示例here

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

    我个人没有发现这种模式很有用。您总是可以使用查找表来实现有限状态机(它不是OO方式,但它运行良好)。

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

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

    应用策略的最后一个案例与称为Replace conditional with polymorphism的重构有关。

    摘要:州和战略解决了非常不同的问题。如果您的问题不能用有限状态机建模,那么可能的状态模式是不合适的。如果你的问题不是封装复杂算法的变体,那么策略就不适用了。

    模式的静态结构

    状态具有以下UML类结构:

    PlantUML class diagram of State Pattern

    策略具有以下UML类结构:

    PlantUML class diagram of Strategy Pattern

    摘要:就静态结构而言,这两种模式大多相同。实际上,this one等模式检测工具会考虑" the structure of the [...] patterns is identical, prohibiting their distinction by an automatic process (e.g., without referring to conceptual information)."

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

    模式的动态

    如上面的问题部分所述,状态表示行为在运行时发生变化,具体取决于对象的某些状态。因此,正如有限状态机的关系所讨论的那样,应用状态转换的概念。 [GoF]提到过渡可以在ConcreteState子类中定义,也可以在集中位置(例如基于表的位置)定义。

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

    PlantUML state transition diagram with two states and one transition

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

    PlantUML sequence diagram for state transitions

    要显示策略的动态,借用real example会很有用。

    PlantUML sequence diagram for strategy transitions

    摘要:每个模式都使用多态调用来根据上下文执行某些操作。在状态模式中,多态调用(转换)通常会导致下一个状态发生更改。在策略模式中,多态呼叫通常不会改变上下文(例如,通过信用卡支付一次并不暗示您下次通过PayPal支付)。同样,状态模式的动态由其相应的 fininte状态机确定,(对我来说)对于纠正这种模式的应用至关重要。

答案 4 :(得分:25)

策略模式涉及从托管类移动算法的实现并将其放在单独的类中。这意味着主机类不需要提供每个算法本身的实现,这很可能导致不洁的代码。

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

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

答案 5 :(得分:13)

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

答案 6 :(得分:13)

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

  • 工作日
  • <强>假日

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

  • 假期:IVR只是回复说'只能在上午9点到下午5点之间的工作日进行通话
  • 工作日:它通过将客户与客户服务主管联系起来作出回应。

将客户与支持主管联系起来的过程本身可以使用策略模式来实施,其中基于以下任何一种方式挑选高管:

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

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

答案 7 :(得分:11)

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

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

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

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

状态模式完全不同。

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

E.g。你有一个登录服务。它接受用户名和密码。它只有一个方法:logon(String userName,String passwdHash)。它不是决定是否接受登录,而是将决策委托给状态对象。该状态对象通常只检查用户/传递组合是否有效并执行登录。但是现在你可以用一个只允许特权用户登录的“Checker”(例如在maintanace时间)或者不允许任何人登录的用户。这意味着“检查器”表示系统的“登录状态”。

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

答案 8 :(得分:8)

策略 模式用于特定任务的多个算法,客户端决定在运行时使用的实际实现。

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

enter image description here

主要功能:

  1. 这是一种行为模式。
  2. 这是基于授权。
  3. 通过修改方法行为来改变对象的内容。
  4. 它用于在算法族之间切换。
  5. 它会在运行时更改对象的行为。
  6. 有关详细信息,请参阅此帖现实世界的例子:

    Real World Example of the Strategy Pattern

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

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

    enter image description here

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

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

    sourcemakingjournaldev文章的主要差异:

    1. State 策略之间的区别在于绑定时间。 策略是绑定一次模式,而状态更具动态
    2. State 策略之间的区别在于intent。 使用策略,算法的选择相当稳定使用State,“context”对象状态的更改会使其从Strategy对象的“调色板”中进行选择
    3. 上下文包含状态作为实例变量,可以有多个任务,其实现可以依赖于状态,而在策略模式 strategy 作为参数传递给方法, context 对象没有任何变量来存储它。

答案 9 :(得分:5)

用外行的语言,

在策略模式中,没有状态或所有状态都具有相同的状态。 所有人都有不同的执行任务的方式,就像不同的医生以不同的方式对待同一患者的相同疾病。

在状态模式中,主观上存在状态,例如患者的当前状态(例如高温或低温),基于该状态将决定下一个行动方案(药物处方)。并且一个状态可以导致其他状态,所以有状态依赖(技术上的组成)。

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

答案 10 :(得分:2)

两种模式都委托给具有多个派生类的基类,但只有在State模式中,这些派生类才能将引用保存回上下文类。

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

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

答案 11 :(得分:2)

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

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

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

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

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

答案 12 :(得分:2)

状态在状态派生类中带有一点依赖性:就像一个状态知道其后的其他状态。例如,夏季来自冬季之后的任何季节状态,或者在存款状态之后的交付状态购物。

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

答案 13 :(得分:1)

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

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

答案 14 :(得分:1)

简而言之,通过策略模式,我们可以动态设置一些行为,使用状态模式,我们可以肯定,一个对象将在内部改变其状态时改变其行为。

答案 15 :(得分:1)

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

答案 16 :(得分: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中,您可以使用不同的算法组合拥有更多状态。状态模式使您可以从一种状态切换到另一种状态,并执行不同的算法组合。

答案 17 :(得分:0)

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

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

答案 18 :(得分:0)

如Wikipedia所述,关于状态模式:

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

让我们谈谈现实世界的例子,它是汽车中的方向盘。方向盘可以更换。我们可以设置更大或更小的方向盘。但是,这不是一个规则,让我们认为,小方向盘会比大方向盘使汽车前轮的角度更大。

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

因此,汽车对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语句的替代方法。

答案 19 :(得分:0)

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

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

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