【问题标题】:how to implement state pattern using ddd如何使用 ddd 实现状态模式
【发布时间】:2014-04-06 10:38:45
【问题描述】:

我对状态模式以及如何使用 DDD 实现它有疑问。 对于我对状态模式的理解,该模式用于处理不同状态之间的转换,因此每个状态都有责任处理系统必须做什么才能转换到新状态。 为此,使用多态性,每个状态都可以是它自己的类,扩展一个基类,实现从一个状态到另一个状态的转换,遵循定义可能转换的图。

我一直在寻找有关如何使用 DDD 执行此操作的信息,并且在其他网站中,我发现了这个使用枚举的网站。

http://nurkiewicz.blogspot.co.uk/2009/09/state-pattern-introducing-domain-driven.html

使用枚举的原因是您可以将当前状态(在本例中为枚举的名称)保存到存储库中,以便稍后重新创建您的聚合。因此,按照状态模式定义,状态可以接收不同的参数(例如,字段的新值),因此上下文(在本例中为聚合)可以转换到新状态并更新其字段,从而使状态保持一致.这意味着,状态将负责更新聚合值。

我的问题是,这样做是否正确?据我了解,在 DDD 中,聚合是知道其内部结构的,并且,每当必须更改聚合内的实体时,聚合必须公开一个方法,该方法稍后会调用实体来更改其值。这样,实体被封装在聚合内,不能直接从聚合外访问。

但是使用状态模式的这种实现,改变聚合的是实体(或值对象,我不知道如何调用状态)。您甚至可以直接使用枚举并在其中调用一个操作,传递您的聚合,聚合将被更改。

关于这个的另一个问题是;当您必须为聚合提供一些取决于当前状态的行为时,您在哪里执行?在聚合内部,向状态的基类添加更多操作以检查状态是一种还是另一种?或者在状态内部,因此状态使用聚合来调用它公开的不同方法以提供该功能?第一种方法意味着,根据您拥有的状态数量,您将创建许多方法来检查您是否处于适合您的目的的正确状态。另一个暗示这又是一个状态,它协调聚合必须调用其内部的方式。

对不起,如果这个问题以前被问过。我已经找了好几天了,但找不到与我在这里问的类似的东西。

提前致谢。

编辑:

抱歉回复晚了,我在放假。

在我的情况下,状态相当简单。它们将反映锦标赛的不同状态(NEW、OPEN_REGISTRATION、CLOSED_REGISTRATION、IN_PROGRESS、FINISHED、CANCELLED)。我正在尝试做的是按照我提供的链接中的方法探索概念。

我的问题是:当聚合(上下文)必须做一些取决于状态的事情(比如注册玩家)时,你会在哪里持有这个逻辑?在上下文中,首先检查状态类是否为 OpenRegistrationState 类型?或者在状态类中,在 OpenRegistrationState 中提供一个实现,将播放器存储到上下文中(该方法需要上下文和播放器作为参数)并从其他的抛出和异常?

似乎使用第二种方法,上下文内部的逻辑非常简单,只需调用当前状态即可完成工作。问题是,当您有许多不同的方法依赖于状态来操作时,您的状态类将有大量不同的方法,其中只有一两个会实现,而其他的会抛出异常。在这种情况下,我不知道是否更容易忘记状态模式,只要使用枚举并在某些逻辑依赖于当前状态时检查值。

【问题讨论】:

  • 你应该阅读领域驱动设计的标签:)
  • 可能重复 stackoverflow.com/q/3975500/1168342(这在您发布的博客的 cmets 中提到过),尽管它也没有答案。
  • 如果你指定一些上下文,它真的很有用。您要建模的状态是什么?我在下面的回答谈到了持久性的对象状态,但这实际上取决于上下文(域对象)和您要建模的状态的性质。

标签: java design-patterns domain-driven-design state-pattern


【解决方案1】:

状态模式的上下文表明对象的行为取决于其状态,其方法包含反映基于状态的条件的 if/then(或 case)逻辑。该模式提供了这些 case 语句的替代方案。

解决方案是为每个状态创建类,实现一个通用接口。依赖于状态的操作从上下文对象委托给它的当前状态对象。上下文对象指向反映其当前状态的状态对象。

所以,如果我理解您的问题,那么域对象(à la DDD)是上下文对象,您希望避免其中的大小写逻辑。否则,您不需要状态模式(如果需要,您现在可以停止阅读,因为其余内容不适用)。

Craig Larman 的应用 UML 和模式第 3 版书中有一章是关于使用模式设计持久性框架,还有一节是关于事务状态和状态模式。我相信他的方法与DDD as it has been defined here 兼容。实际上,它是PersistentObject idea from Scott Ambler的变体

域类有状态。在此示例中,状态与持久性有关:NewOldCleanOldDirtyOldDeleteDeleted。有关详细信息,请参阅 Larman 的书。

我无法在上面的 PlantUML 图像上轻松注释的几个细节:

  • PObjectState 抽象类对每个方法(转换)都有默认的无操作体
  • 实现,例如,OldDirtyState.commit(...) 根据状态处理行为的实现。对于持久性,这是有道理的,因为它们通常是与域逻辑无关的行为。持久化对象的回滚基本相同。

Larman 有一个脚注指出“只要域对象类扩展了技术服务类,就应该暂停以进行反思,因为它混合了架构问题(持久性和应用程序逻辑)。”

您没有使用具体示例来说明您想要建模的状态到底是什么,所以我不确定这个示例是否适用。

如果您的状态是一些横切关注点,会在您的域对象中引入不需要的耦合,那么您必须问自己一个更糟糕的问题:域对象中的案例逻辑或不需要的耦合。设计是权衡,你必须选择你的战斗。

【讨论】:

  • 抱歉回复晚了,我在放假。你能检查一下编辑的部分吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-11
  • 1970-01-01
相关资源
最近更新 更多