Как я должен полностью моделировать объект с несколькими состояниями?

StackOverflow https://stackoverflow.com/questions/2134328

  •  22-09-2019
  •  | 
  •  

Вопрос

У меня есть объект, эквивалентный задаче, который может иметь несколько состояний."Задача" может находиться либо в состоянии ожидания, либо в состоянии прохождения, либо в состоянии сбоя.Каждое из этих состояний также будет содержать некоторые уникальные данные.Например, в состоянии сбоя у объекта должна быть причина сбоя, в состоянии ожидания у него должен быть крайний срок для оценки и т.д.

Хотя вышесказанное наводит меня на мысль, что у меня должен быть отдельный объект для представления каждого состояния, базовый идентификатор объекта должен оставаться прежним, что подталкивает меня к мысли об этом как о едином объекте.

Кроме того, переход из состояния в состояние требует определенной логики."Ожидающая" задача, переходящая в состояние "пройдено", будет обрабатываться иначе, чем "неудачная" задача, выполняющая тот же переход.

Если бы представления для каждого состояния были точно такими же, я бы просто использовал примитивное свойство и покончил с ним.Однако, поскольку разные состояния имеют немного разные представления, я изо всех сил пытался найти наилучший способ смоделировать это.Логика управления внутренним состоянием становится немного запутанной, поэтому я решил сделать шаг назад и пересмотреть.Есть какие-нибудь мысли?

Я использую c #, хотя я бы счел этот язык независимым.

Это было полезно?

Решение

Когда я впервые прочитал этот вопрос, моим ответом было использовать перечисление для определения состояния.Однако, перечитав его, я бы предложил одно из следующих:

  1. Реализуйте каждую задачу как отдельный класс (PendingTask, PassedTask, ...) с тем же родителем и конструктором, который принимает задачу любого типа, которая предшествует этому состоянию.
  2. Реализуйте одну задачу и создайте новый класс TaskStateData с дочерними классами для данных, необходимых для каждого состояния.
  3. Реализуйте одну задачу, но имейте отдельный метод для изменения состояния для каждого типа состояния с параметрами для дополнительных атрибутов, необходимых для этого состояния

Я предлагаю эти решения для обеспечения целостности данных.

Другие советы

Использование чисто объектно-ориентированного подхода имеет свои недостатки.Если вы не планируете выполнять много операций с полимофическим кодом, избегайте прямого представления состояния вашего доменного класса с помощью полиморфизма.Я остановился на более гибридном подходе.Различные состояния должны быть смоделированы как отдельное дерево наследования родительских / дочерних элементов.Начните с абстрактного базового класса MyState, который имеет в качестве дочерних элементов MyState1, MyState2 и MyState3.( см . Ответ Джеффри для примера.

Объект, для которого необходимо отслеживать его состояние, имеет атрибут 'current-state' типа MyState.когда объект меняет состояние, это простое присваивание или вызов setter() для его изменения.При желании вы можете создавать одноэлементные экземпляры каждого состояния или создавать новые экземпляры для каждого изменения состояния.Это зависит от того, как часто меняется состояние и у скольких объектов отслеживается их состояние.Если цифры становятся слишком большими, вы можете рассмотреть синглетный подход.

"Задача" может находиться либо в состоянии ожидания, либо в состоянии прохождения, либо в состоянии сбоя."Ожидающая" задача, переходящая в состояние "пройдено", будет обрабатываться иначе, чем "неудачная" задача, выполняющая тот же переход.

Это кажется довольно странным набором состояний.Я бы ожидал, что выполнение задачи займет некоторое время, поэтому переход от ожидания к выполнению затем либо завершается, либо завершается неудачей, а задачи, которые не выполняются до истечения их времени, истекают.Если также существует переход от failed к failed-and-expired, то это может добавить другое состояние.

Нарисуйте конечный автомат, чтобы найти состояния.

Во-первых, нужно ли вам структурно моделировать состояния?Подойдет ли флаг ожидания / истечения срока действия, запланированное время и результат (с ошибкой и успехом в качестве двух подтипов результата)?Что нужно от этого клиентам задачи?

Во-вторых, взаимодействуете ли вы с задачей или планировщиком?Нередко можно передать описание задачи планировщику и получить обратно будущее, к которому можно запросить результат выполнения задачи.Но сама задача не раскрывается, только то, выполнена ли она и какой результат.Если вам требуется прогресс, вы можете захотеть иметь планировщик, который можно запрашивать по идентификатору задачи для получения прогресса, а не объект task, на который у вас есть ссылка - наличие объекта task, который одновременно изменяет состояние, затрудняет получение из него согласованного набора данных о состоянии, пока он не достигнет конечного состояния.Если состояние "передано" не содержит информации об ошибке, то запрос "вы потерпели неудачу", за которым следует "получить статус сбоя", легко приводит к гонке, если вы не примените внешнюю блокировку (ewww), поэтому становится желательным атомарный возврат неизменяемого объекта информации о состоянии задачи, и к этому времени ваш объект задачи становится более или менее эквивалентным идентификатору, переданному планировщику.

Взгляните на паттерн состояния.

Это звучит как идеальное применение объектного наследования и полиморфизма.

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

}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top