我应该如何为具有多个状态的实体建模?

时间:2010-01-25 17:50:29

标签: modeling domain-model

我有一个实体,相当于一个任务,可以有多个状态。 “任务”可以处于挂起,通过或失败状态。这些状态中的每一个也将具有一些独特的数据。例如,在失败的状态下,实体应该有失败的原因,在待决状态下它应该有一个评估的最后期限等。

虽然上面让我认为我应该有一个单独的对象来表示每个状态,但实体的底层ID应该保持不变,这让我回想起这个单个对象。

此外,从州到州的过渡需要一些逻辑。转换为“已通过”状态的“待处理”任务将与执行相同转换的“失败”任务区别对待。

如果每个州的表示完全相同,我只使用原始属性并完成它。但是,由于不同的州有不同的表现形式,我一直在努力找出最好的方法来模拟这种情况。管理内部状态的逻辑变得有点混乱所以我想我退后一步并重新考虑。有什么想法吗?

虽然我认为这种语言不可知,但我正在使用c#。

5 个答案:

答案 0 :(得分:1)

当我第一次阅读这个问题时,我的回答是使用枚举来定义状态。但是在重读之后,我会建议以下之一:

  1. 将每个任务实现为具有相同父级的单独类(PendingTask,PassedTask,...),以及接受在该状态之前的任何类型的任务的构造函数。
  2. 实现一个Task,并为每个状态所需的数据创建一个带有子类的新类TaskStateData。
  3. 实现一个任务,但有一个单独的方法来更改每个状态类型的状态,并带有该状态所需的额外属性的参数
  4. 我建议使用这些解决方案来确保数据的完整性。

答案 1 :(得分:1)

采用纯面向对象的方法有缺点。除非您计划进行大量的多态代码管理,否则请避免直接使用多态来表示域类的状态。我已经采用了更加混合的方法。需要将不同的状态建模为单独的父/子继承树。从一个抽象基类MyState开始,它具有子项MyState1,MyState2和MyState3。 (有关示例,请参阅Jeffrey's answer

需要跟踪状态的实体具有“当前状态”属性,属性类型为MyState。当实体改变状态时,它是一个简单的赋值或setter()调用来改变它。如果需要,您可以构造每个状态的单例实例,或者为每个状态更改构造新实例。这取决于状态变化的频率以及跟踪状态的对象数量。如果数字太大,你可以考虑单例方法。

答案 2 :(得分:1)

  

“任务”可以处于挂起,通过或失败状态。   转换为“已通过”状态的“待处理”任务将与执行相同转换的“失败”任务区别对待。

这似乎是一个相当奇怪的国家集合。我希望一个任务需要一些时间来执行,所以从挂起转换到执行然后通过或失败,以及在它们的时间用完之前没有执行的任务到期。如果还有从失败到失败和过期的转换,那么可能会添加另一个状态。

绘制状态机以查找状态。

首先,您是否需要在结构上对状态进行建模?挂起/过期标志,预定时间和结果是否会成功(失败和成功作为结果的两个子类型)?这项任务的客户需要从中获得什么?

其次,您是在与任务或调度程序进行交互吗?向调度程序提供任务描述并返回未来可能会查询任务结果的情况并不少见。但任务本身并未暴露,只是它是否完整和结果。如果您需要进度,您可能希望有一个可以通过任务ID查询以获得进度的调度程序,而不是您持有引用的任务对象 - 具有同时更改状态的任务对象使得难以获得一致的集合来自它的状态数据,直到它达到最终状态。如果'通过'状态没有失败信息,那么查询'你是否失败',然后'获取失败状态'很容易导致比赛,除非你外部锁定(ewww),因此原子地返回一个不可变的任务状态信息对象期望,此时您的任务对象或多或少等同于传递给调度程序的ID。

答案 3 :(得分:0)

答案 4 :(得分:0)

这听起来像是对象继承和多态的理想应用。

abstract class Task
{
      public int TaskId { get; private set; }
      abstract PassedTask TransitionToPassed();
      ...
}

class PendingTask : Task
{
      PassedTask TransitionToPassed()
      {
            PassedTask passed = new PassedTask();
            passed.TaskId = TaskId;
            ...
            return passed;
      }
      ...
}

class PassedTask : Task
{
      PassedTask TransitionToPassed()
      {
            return this;
      }
      ...
}

class FailedTask : Task
{
      public string ReasonForFailure { get; private set; }
      PassedTask TransitionToPassed()
      {
            ...
      }
      ...

}