如何使用ddd实现状态模式

时间:2014-03-04 18:10:03

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

我对状态模式以及如何使用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中提供一个实现,它将播放器存储到上下文中(该方法将期望上下文和播放器作为参数)并抛出其他的播放和异常?

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

1 个答案:

答案 0 :(得分:1)

状态模式的上下文表示对象的行为依赖于其状态,并且其方法包含if / then(或case)逻辑,反映基于状态的条件。该模式提供了这些案例陈述的替代方案。

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

所以,如果我理解你的问题,那么Domain对象(例如DDD)就是上下文对象,你想要避免使用案例逻辑。否则,您不需要状态模式(如果需要,您可以立即停止阅读,因为其余部分不适用)。

Craig Larman的应用UML和模式第3版本书有一章关于设计带有模式的持久性框架以及关于事务状态和状态模式的部分< / em>的。我相信他的方法与DDD as it has been defined here兼容。实际上,它是PersistentObject idea from Scott Ambler

的变体

State Pattern applied for persistent domain object

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

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

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

Larman有一个脚注说明“每当一个域对象类扩展一个技术服务类时,它应该暂停反思,因为它混合了架构问题(持久性和应用程序逻辑)。”

你没有使用一个具体的例子来确定你想要建模的状态,所以我不确定这个例子是否适用。

如果你的状态是一个跨领域的问题,在域对象中引入了不必要的耦合,那么你必须问自己更糟糕的问题:Domain对象中的Case逻辑,或者不需要的耦合。设计是权衡,你必须选择你的战斗。