【问题标题】:How should I model an entitly with multiple states?我应该如何为具有多个状态的实体建模?
【发布时间】:2010-01-25 17:50:29
【问题描述】:

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

虽然上述内容让我认为我应该有一个单独的对象来表示每个状态,但实体的底层 ID 应该保持不变,这促使我重新将其视为单个对象。

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

如果每个状态的表示完全相同,我将只使用原始属性并完成它。然而,由于不同的状态有略微不同的表示,我一直在努力找出最好的建模方法。管理内部状态的逻辑有点混乱,所以我想我会退后一步重新考虑。有什么想法吗?

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

【问题讨论】:

    标签: modeling domain-model


    【解决方案1】:

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

    1. 将每个任务实现为具有相同父级的单独类(PendingTask、PassedTask...),以及接受该状态之前的任何类型任务的构造函数。
    2. 实现一个任务,并为每个状态所需的数据创建一个带有子类的新类 TaskStateData。
    3. 实现一个任务,但有一个单独的方法来更改每种状态类型的状态,并带有该状态所需的额外属性的参数

    我建议使用这些解决方案来确保数据完整性。

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案3】:

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

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

        绘制状态机来查找状态。

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

        其次,您是在与任务还是调度程序交互?给调度器一个任务描述并返回一个未来,这可以查询任务的结果,这并不少见。但任务本身并没有暴露,只有是否完成和结果。如果您需要进度,您可能需要一个可以通过任务 ID 查询以获取进度的调度程序,而不是您持有引用的任务对象 - 拥有一个同时更改状态的任务对象很难获得一致的集合状态数据,直到它达到最终状态。如果“通过”状态没有失败信息,那么查询“你失败了吗”,然后查询“获取失败状态”很容易导致竞争,除非您将锁定 (ewww) 外部化,因此以原子方式返回不可变的任务状态信息对象变为可取的,到那时您的任务对象或多或少等同于传递给调度程序的 ID。

        【讨论】:

          【解决方案4】:

          【讨论】:

          • 我熟悉这种模式。根据我的理解以及我使用它的方式,它最有助于在运行时更改对象的行为。对于这个问题,我认为更多的是管理对象表示的差异。我可能在这里吠叫错误的树。仅仅解释问题就帮助我确定了一些不相容的目标。
          【解决方案5】:

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

          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()
                {
                      ...
                }
                ...
          
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-11-24
            • 2013-10-05
            • 1970-01-01
            • 2011-08-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多