策略模式应该是无状态的吗?

10
一个“四人帮策略模式”的类是否必须完全无状态(即没有字段),还是可以包含不可变状态(即final字段)?

1
策略模式为什么需要状态?它的工作是返回一些“可插拔”的功能。 - Mitch Wheat
如果您有一个日志记录策略,其中“briefLoggingStrategy”是“如果我之前看过这条消息,我将输出“ditto”...否则输出整个消息”,那么您的策略必须保持状态。 - Nigel Thorne
5个回答

18

策略类封装的是行为而非物体。所以虽然你如果真的必须这样做,但在其中保留状态并没有太多意义。把它看作一个动词,而不是名词。或至少是描述一个行动的名词。另一方面,您始终可以将策略参数化,并通过方法调用或类似方式将客户端对象的状态传递给它。

此外,类是无状态还是有状态并不取决于其字段上的final关键字。例如:

  • 在对象上使用final仅表示不能更改对对象的引用。您仍然可以更改该对象的内容,例如其字段。在这种情况下,您的类具有状态,尽管其字段是 final 的。
  • 如果字段确实是常量,则在大多数意义上,您可以认为它是无状态的。例如,您可以声明一个private static final字段,然后确保您永远不会对所引用对象的状态进行任何更改。

编辑:让我们试着区分策略本身的参数和一次执行该策略的特定参数。如果策略类是无状态的,则没有必要拥有超过一个该类的实例,这使得对象而非类本身代表行动,而方法执行代表该策略的一个特定执行。

现在,如果我们有一个策略本身的参数,那么这个参数对于所有执行该策略的操作来说都应该具有相同的值。因此,它可以放在常量中,方法调用返回常量(如果我们不想使用静态内容),甚至是硬编码。如上所述,这可以以无状态的方式实现。

另一方面,如果参数描述了策略的一个特定执行,则只需将其作为参数传递给方法即可。就这样。

附加说明:如果我们希望使用带有“副词”打包在其状态中的动作对象,例如带有延迟、调度等执行队列,则使用 Command 模式是有充分理由的。


并不是我不同意你的观点(事实上,我还支持你!+1),但我认为值得注意的是,即使是动词/行为也有描述词。如果你将一个有状态的对象看作名词,那么它本质上就有形容词。同样地,即使你的策略类封装了一个动作或动词,你仍然可以假设使用“副词”来详细说明该动作的具体细节。 - JMTyler
@JMTyler:我从未将操作参数视为副词。谢谢你的比喻!请查看我的编辑以获取回复。 - Goran Jovic
太酷了!我以前没听说过命令模式,谢谢你指出来! - JMTyler

6
不,为什么一定要是无状态的呢?策略可以是任何东西,它只是代表了一些运行时可插拔的功能单元,允许您扩展并修改消费类的行为。据我所知,并没有任何要求它必须是无状态或不可变的。

1
你是在说策略就是一个委托吗? - murungu
1
现在我们进入了语义学。从词汇“delegate”的最广义意义上来说,是的;但从具体的.NET意义上来说,却不是。 - MalcomTucker

5
无状态是指在策略的多次运行中,没有数据保留下来;也就是说,如果您执行两次相同的策略,上一次执行的任何内容都不会被保留。这有利于在需要时避免“重置”策略实现。
请注意,在策略模式实现的描述中,它们提到了一个包含策略执行所需数据的上下文(在我的书中第317页)。所有实现所需的“状态”应该放在这些上下文对象中。
这意味着,策略实现本身是无状态的,但整个模式具有状态,因为所需的数据在上下文中传递。
例如,如果您有执行数学运算的策略实现,至少有两种方法可以实现。第一种方法是在构建策略实现时设置arg1和arg2(以及3、4等),然后当您执行实现时,它将获取其字段并执行操作。问题是,如果再次运行相同的实现,您必须重置所有的字段(或创建一个新的实现)。
第二种方法是创建一个包含所有参数的上下文。策略实现将从上下文中获取其所需的值,然后每次只需传递一个新的上下文即可重用策略实现的每个实例。不需要担心重新创建新的实现或忘记重置实现实例。当然,您仍然需要正确地管理上下文。

如果您有一个日志记录策略,其中“briefLoggingStrategy”是“如果我之前看过这条消息,我将输出“ditto”...否则输出整个消息”,那么您的策略必须保持状态。 - Nigel Thorne

3

策略模式的目的是直接在方法的本地范围内处理策略方法参数。当然,策略类本身可以在需要时保留一些字段,但绝不能将方法参数分配给这些字段,因为如果策略对象被多次使用,可能会引入线程安全问题。


0

是的,因为这主要是算法选择器。具体的算法可以有状态,但选择器没有。

顺便说一下,在这个策略中我们没有一个类。你指的是哪个具体的类?在什么角色下?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接