Pergunta

Eu tenho uma entidade, equivalente a uma tarefa, que pode ter vários estados. A "tarefa" pode estar em um estado pendente, passado ou fracassado. Cada um desses estados também terá alguns dados exclusivos. Por exemplo, em um estado fracassado, a entidade deve ter um motivo para o fracasso, no estado pendente, deve ter um prazo para avaliação, etc.

Enquanto o acima me leva a pensar que eu deveria ter um objeto separado para representar cada estado, o ID subjacente para a entidade deve permanecer o mesmo, me empurrando de volta a pensar nisso como um único objeto.

Além disso, a transição de estado para estado requer alguma lógica. Uma tarefa "pendente" que a transição para um estado "aprovada" será tratada de maneira diferente da tarefa "falhada", fazendo a mesma transição.

Se as representações para cada estado fossem exatamente as mesmas, eu usaria uma propriedade primitiva e faria com ela. No entanto, como os diferentes estados têm representações ligeiramente diferentes, tenho lutado para descobrir a melhor maneira de modelar isso. A lógica para governar o estado interno está ficando um pouco bagunçada, então achei que eu daria um passo atrás e reconsiderar. Alguma ideia?

Estou usando C# embora considere esse idioma agnóstico.

Foi útil?

Solução

Quando li essa pergunta pela primeira vez, minha resposta foi usar uma enumeração para definir o estado. Depois de reler, porém, eu sugeriria um dos seguintes:

  1. Implemente cada tarefa como uma classe separada (PendingTask, PassedTask, ...) com o mesmo pai e um construtor que aceita uma tarefa de qualquer tipo que seja antes desse estado.
  2. Implemente uma tarefa e crie uma nova classe TaskStatedata com classes infantis para os dados necessários para cada estado.
  3. Implemente uma tarefa, mas tenha um método separado para alterar o estado para cada tipo de estado com parâmetros para os atributos extras necessários para esse estado

Sugiro essas soluções para garantir a integridade dos dados.

Outras dicas

Ir com uma abordagem pura orientada a objetos tem desvantagens. A menos que você planeje ter que fazer muito código polimófico, evite representar o estado do seu domínio usando o polimorfismo diretamente. Eu resolvi uma abordagem mais híbrida. Os diferentes estados precisam ser modelados como uma árvore de herança de pai/filho separada. Comece com um mystate abstrato da classe base que tem como filhos MyState1, MyState2 e MyState3. ( Vejo Resposta de Jeffrey Por exemplo.

A entidade que precisa de seu estado rastreada possui um atributo de 'estado atual', que é do tipo Mystate. Quando a entidade muda afirma, é uma atribuição simples ou a chamada setter () para alterá -la. Você pode construir instâncias de singleton de cada estado, se assim desejar, ou construir novas instâncias para cada mudança de estado. Depende da frequência com que o estado muda e de quantos objetos estão tendo seu estado rastreado. Se os números ficarem muito grandes, você pode considerar a abordagem de singleton.

A "tarefa" pode estar em um estado pendente, passado ou fracassado. Uma tarefa "pendente" que a transição para um estado "aprovada" será tratada de maneira diferente da tarefa "falhada", fazendo a mesma transição.

Isso parece uma coleção bastante estranha de estados. Eu esperaria que uma tarefa levasse algum tempo para executar, portanto, a transição de pendente para a execução e depois passa ou falhe e tarefas que não são executadas antes que o tempo acabasse para expirar. Se houver também uma transição de falhou em falhar e expirado, isso pode adicionar outro estado.

Desenhe uma máquina de estado para encontrar os estados.

Em primeiro lugar, você precisa modelar os estados estruturalmente? Uma bandeira pendente/expirada, um tempo agendado e um resultado (com falha e sucesso como os dois subtipos de resultado)? O que os clientes da tarefa precisam disso?

Em segundo lugar, você está interagindo com uma tarefa ou um agendador? Não é incomum dar uma descrição de tarefa a um agendador e recuperar um futuro, que pode ser consultado sobre o resultado da tarefa. Mas a tarefa em si não está exposta, apenas se está completa e o resultado. Se você precisar de progresso, convém ter um agendador que possa ser consultado pelo ID da tarefa para obter progresso, em vez de um objeto de tarefas para o qual você mantém uma referência - ter um objeto de tarefa que muda o estado simultaneamente dificulta a obtenção de um conjunto consistente de dados do estado, até atingir um estado final. Se o estado 'aprovado' não tiver informações de falha, então a consulta 'você falhou', seguida de 'obter o status de falha', levará facilmente a uma corrida, a menos que você externise o bloqueio (ewww), portanto, devolver um objeto de informação de estado de tarefa imutável se tornar atomicamente desejável, quando seu objeto de tarefa se torna mais ou menos equivalente ao ID passado para um agendador.

Dê uma olhada no padrão de estado.

Isso soa como uma aplicação ideal de herança e polimorfismo de objetos.

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

}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top