战略设计模式与国家设计模式的区别是什么?

战略设计模式和国家设计模式之间的区别是什么?我在网上浏览了不少文章,但看不出明显的区别。

有人能用外行的语言解释一下吗?

92988 次浏览

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

区别在http://c2.com/cgi/wiki?StrategyPattern中讨论。我使用Strategy模式允许在分析数据的总体框架中选择不同的算法。通过这种方式,您可以添加算法,而不必更改整个框架及其逻辑。

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

    策略模式实际上是关于有一个不同的 实现,完成(基本上)相同的事情,所以 一个实现可以根据策略的需要替换另一个实现。 例如,你可能有不同的排序算法 策略模式。对象的调用者不会基于 采用的是哪种策略,但不管策略的目标是什么 是相同的(排序集合). 状态模式是关于基于 同时让打电话的人从电话的负担中解脱出来 容纳所有可能的状态。例如,你可能有一个 getStatus()方法返回不同的状态 对象的状态,但方法的调用者不必是 . .

这两种模式都委托给一个基类,这个基类有几个派生类,但是只有在State模式中,这些派生类才保留对上下文类的引用。

从另一个角度来看,战略模式是国家模式的简单版本;如果你愿意,也可以称之为子模式。这实际上取决于你是否希望派生状态保留对上下文的引用(即:你是否希望它们调用上下文上的方法)。

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

策略模式涉及到将算法的实现从宿主类移到单独的类中。这意味着宿主类不需要提供每个算法本身的实现,这很可能导致不干净的代码。

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

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

策略:策略是固定的,通常由几个步骤组成。(排序只构成了一个步骤,因此是一个非常糟糕的例子,因为它太原始了,无法理解此模式的目的)。 策略中的“主”例程调用了一些抽象方法。如。“进入房间策略”,“main-method”是goThroughDoor(),它看起来像:approachDoor(), if (locked()) openLock();openDoor ();enterRoom ();把();closeDoor ();if (wasLocked()) lockDoor();

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

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

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

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

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

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

最重要的区别是:当你选择了一种策略,你要坚持它,直到你完成它。这意味着你调用它的“主方法”,只要它在运行,你就永远不会改变策略。OTOH在系统运行时的状态模式情况下,您可以随意更改您认为合适的状态。

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

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

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


关于它们的优点:

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

通过使用策略模式,算法使用(上下文)类从如何的知识中解脱出来,以执行特定的任务(——“算法”)。这种情况也创建了对OCP的坚持;这个类对于如何执行这个任务的改变是关闭的,但是设计是非常开放的,可以添加其他算法来解决这个任务 这可能还提高了上下文类对单一责任原则(SRP)的坚持。此外,该算法很容易被其他类重用

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

  • 状态存储对包含它们的上下文对象的引用。战略则不然。
  • 状态可以替换自己(IE:将上下文对象的状态更改为其他状态),而策略则不能。
  • 策略作为参数传递给上下文对象,而状态由上下文对象本身创建。
  • 策略只处理一个特定的任务,而状态为上下文对象所做的所有(或几乎所有)事情提供底层实现。

一个“经典”的实现将匹配列表中的每个道具的状态或策略,但你也会遇到混合了两者的情况。具体是国家层面的还是战略层面的,最终是一个主观问题。

考虑一个处理客户呼叫的IVR(交互式语音应答)系统。你可能想要编程它来处理客户:

  • 工作日
  • 假期

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

  • 假期: IVR简单地回应说'只能在工作日上午9点至下午5点之间接听电话'。
  • 工作日:它通过连接客户到客户服务执行来响应。

这个连接客户和支持执行者的过程本身可以使用策略模式来实现,其中执行者是根据以下任何一个来挑选的:

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

策略模式决定'如何'来执行某些操作,状态模式决定''来执行这些操作。

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

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

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

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

让我们比较国家和战略。

模式解决的问题

状态用于以下两种情况之一:

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

如果你想确保你确实有State模式解决的问题,你应该能够使用有限状态机来建模对象的状态。你可以找到一个应用的例子在这里

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

我个人认为这种模式并不有用。您总是可以使用查找表实现有限状态机(这不是面向对象的方法,但它工作得非常好)。

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

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

在哪里应用Strategy的最后一种情况与称为用多态性替换有条件的的重构有关。

状态和策略解决的问题完全不同。如果您的问题不能用有限状态机建模,那么可能的状态模式就不合适。如果您的问题不是关于封装复杂算法的变体,那么Strategy就不适用。

模式的静态结构

状态具有以下UML类结构:

PlantUML class diagram of State Pattern

策略具有以下UML类结构:

PlantUML class diagram of Strategy Pattern

简介:在静态结构方面,这两个模式基本上是相同的。事实上,像这一个这样的模式检测工具认为"是对象的结构 […]模式是相同的,禁止他们 通过自动过程进行区分(例如,不引用 "

.

然而,如果ConcreteStates自己决定状态转换(参见上图中的"可能会决定"关联),则会有很大的不同。这导致了具体状态之间的耦合。例如(请参阅下一节),状态A决定到状态b的转换。如果Context类决定到下一个具体状态的转换,这些依赖关系就消失了。

模式的动态性

正如上面的Problem部分所提到的,状态意味着在运行时行为的改变取决于对象的某些状态。因此,状态过渡的概念适用,就像讨论有限状态机的关系一样。[GoF]提到转换可以定义在ConcreteState子类中,也可以定义在一个集中的位置(比如基于表的位置)。

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

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

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

PlantUML状态转换序列图

为了显示策略的动态,可以借用真实的例子

PlantUML策略转换序列图

总结:每个模式使用一个多态调用来做一些取决于上下文的事情。在State模式中,多态调用(转换)通常会导致下一个状态发生变化。在Strategy模式中,多态调用通常不会改变上下文(例如,用信用卡支付一次并不意味着下次要用PayPal支付)。同样,State模式的动态是由其对应的有限状态机,决定的,(对我来说)这对正确应用此模式至关重要。

用外行的话来说,

在策略模式中,没有状态或所有状态都相同。 每个人都有不同的方法来完成一项任务,就像不同的医生用不同的方法来治疗同一种疾病,同一种状态的同一病人。< / p >

在state Pattern中,主观上存在状态,如患者当前的状态(如高温或低温),据此决定下一步的行动(药物处方)。一个状态可以导致另一个状态,因此存在状态之间的依赖关系(技术上来说是组合)。

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

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

来自维基策略模式的UML图

enter image description here

主要特点:

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

参考这篇文章了解更多信息&现实世界的例子:

策略模式的真实例子

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

来自维基状态模式的UML图

enter image description here

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

更多细节请参考这篇journaldev文章。

关键差异从sourcemakingjournaldev文章:

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

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

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

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

在策略模式的情况下,类图的安排与状态模式相同。客户来此安排做一些操作。也就是说,不同的算法取代了不同的状态,比如需要对模式进行不同的分析。在这里,客户端告诉上下文它想做什么,用什么算法(业务定义的自定义算法),然后执行。

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

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

但不同之处在于它们使用的是基于对象状态执行不同逻辑的状态模式。在战略的案例中有不同的逻辑。

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

另一方面,策略没有这样的依赖关系。在这里,任何类型的状态都可以基于程序/产品类型进行初始化。

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

当你有一个可以分为两个任务的项目时:

任务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中使用不同的算法组合获得更多的状态。状态模式允许从一种状态切换到另一种状态,并执行不同的算法组合。

“策略”只是一种算法,你可以根据你的需要在不同的情况下改变它,它为你处理一些事情。 例如,你可以选择如何压缩一个文件。Zip or rar…

但是“State”可以改变你所有的对象行为,当它改变时, 甚至它也能改变其他领域……这就是为什么它有一个指向它的主人的引用。您应该注意到,改变对象字段完全可以改变对象行为。 例如,当你在obj中将stat0更改为State1时,你将一个整数更改为10。因此,当我们调用obj.f0()来进行一些计算并使用该整数时,它会影响结果
在我看来,主要的区别在于他们的意图。从技术上讲,StateStrategy模式看起来非常相似。 主要区别在于:

  • State模式在需要时更改上下文的状态,并且状态可以多次更改。所以上下文改变其状态或状态可以设置另一个状态
  • 策略模式集策略和策略可以改变很少和环境不会改变它的策略

策略模式。

我们抽象出一些合理的策略:

public interface ISound
{
void Make();
}
    

及其具体策略:

public class DogSoundStrategy : ISound
{
public void Make()
{
Console.WriteLine("Bar");
}
}


public class CatSoundStrategy : ISound
{
public void Make()
{
Console.WriteLine("Meow");
}
}

这是对能发声的Animal的抽象描述:

public abstract class Animal
{
public void MakeSound(ISound sound)
{
sound.Make();
}
}

具体的动物是这样的:

public class Dog : Animal
{
}


public class Cat : Animal
{
}

然后我们可以像这样调用上面的代码:

Dog dog = new Dog();
dog.MakeSound(new DogSoundStrategy()); // there is a small chance
// that you want to change your strategy


Cat cat = new Cat();
cat.MakeSound(new CatSoundStrategy()); // there is a small chance
// that you want to change your strategy

有一个小的机会,你想要改变你的策略

状态模式

想象一下,你有一个电脑游戏,英雄可以是世界上任何一个超级人物。让我们称他为Hero。他能跑、能游、能飞,还能把自己的形状变成IronManSpiderMan。你有一个按钮,你可以改变它的形状或状态为IronManSpiderMan

英雄的代码是这样的:

public class Hero
{
IState _state;


public Hero()
{
_state = new SpiderManState();
}


public void Run()
{
_state.Run();
}


public void Swim()
{
_state.Swim();
}


public void Fly()
{
_state.Fly();
}


public void ChangeShape()
{
_state = _state.SetShape();
}
}

IState的接口看起来像这样:

public interface IState
{
void Run();


void Swim();


void Fly();


IState SetShape();
}

具体的状态是这样的:

public class SpiderManState : IState
{
public void Fly()
{
Console.WriteLine("Spiderman is flying");
}


public void Run()
{
Console.WriteLine("Spiderman is running");
}


public void Swim()
{
Console.WriteLine("Spiderman is swimming");
}


public IState SetShape()
{
return new IronManState();
}
}

IronManState会是这样的:

public class IronManState : IState
{
public void Fly()
{
Console.WriteLine("IronMan is flying");
}


public void Run()
{
Console.WriteLine("IronMan is running");
}


public void Swim()
{
Console.WriteLine("IronMan is swimming");
}


public IState SetShape()
{
return new SpiderManState();
}
}
现在通过点击Hero类的ChangeShape()按钮,你将能够改变英雄的State从, 例如:SpiderManIronMan.

因此,上下文状态(Hero)依赖于它的按钮ChangeShape,并可以通过它来更改。这种情况会发生很多次。

有一个高的机会,你想改变上下文的状态。

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

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

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

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

    在执行搜索时的策略模式中,我们可以有 多种搜索策略,例如NaiveStrategy()KMPStrategy()RabinKarp()策略。这些都是独立的 有些稳定的选择。最重要的是,策略不能改变 彼此之间。只有Context能够改变策略 另一方面,状态模式是基于Finite-State的概念 Machines。状态可以相互转换。这里的状态 比策略更不稳定。每个人都有一件事 具体状态维护context的引用,因此能够 .转换到另一个状态

因此,关键在于在策略中,只有上下文可以设置策略,而在状态模式下,状态可以转换到其他状态。策略在策略模式中是彼此的不知道。而在状态模式中,状态并不是不知道彼此,并且在它们维护对上下文对象的引用时允许转换。

策略使这些对象完全独立,彼此不知道。然而,State不限制具体状态之间的依赖关系,允许它们随意改变上下文的状态。

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