Como deve ser um objeto usando o padrão estado ser transferida para o próximo estado?

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

  •  09-09-2019
  •  | 
  •  

Pergunta

Eu tenho uma classe Order, que passa por uma série de estados definidos. Para ajudar com isso, eu ter implementado o padrão de Estado de tal forma que o objeto Ordem tem um membro CurrentState que implementa uma interface IOrderState. Em seguida, tenho implementações concretas dessa interface, como OrderStateNew, OrderStateDelivered etc etc

A minha pergunta é, qual é a maneira correta para a transição do objeto Order entre os estados? É aceitável ter um método Order.SetState () que permite que um serviço externo para definir o estado? Os critérios que determinam as mudanças de estado são armazenadas externamente ao objeto Order modo que este parece a resposta óbvia, mas estou um pouco desconfortável em ter um método público no meu objeto de mudar algo tão fundamental como este.

esclarecimentos adicionais Eu pensei que poderia ser útil para adicionar mais alguns detalhes sobre minha implementação, porque eu me pergunto se eu estou realmente usando o padrão corretamente em primeiro lugar. Aqui está a API pulbic para criar e autorizar uma ordem

Dim orderFacade As New OrderFacade
Dim order = orderFacade.createFrom(customer)

' Add lines etc

' This will validate the order and transition it to status 'Authorised'
Dim valid = orderFacade.Authorise(order)

' This will commit the order, but only if it is at status 'Authorised'
Dim result = orderFacade.Commit()

O OrderFacade.Authorise () função é algo como isto

Public Function Authorise(ByRef originalOrder As Order) As ValidationSummary

    If originalOrder.CurrentState.CanAuthorise() Then

       Dim validator = OrderValidatorFactory.createFrom(originalOrder)
       Dim valid = validator.ValidateOrder(originalOrder)

      If valid.IsValid Then
          originalOrder.SetOrderStatus(OrderStatus.Authorised)
      End If

     Return valid

    End If

 End Function

Como você pode ver, o membro CurrentState é a implementação atual IOrderState que determina quais atividades são válidos para o objeto. Gostaria de saber se esta deve ser responsável por determinar a transição em vez do OrderFacade?

Foi útil?

Solução

Considere alterar o estado, por implicação, ao invés de atribuição.

Em quase todos os casos que eu já vi, o Estado pode ser inferida a partir de outras propriedades (espero dentro da classe). Se assim for, não persistem o estado, mas derivam-lo quando necessário. Caso contrário, muitas vezes você vai acabar com desacordos problemáticas entre valores inferidos e atribuídos. (E na minha experiência "derivado" é invariavelmente direita.)

(A complexidade muitas vezes é rever o log de transações para a classe, e considerar o evento mais recente (s). Mas vale a pena mesmo assim.)

Outras dicas

A SetState () método teria vantagens em estender ordens com outros estados, bem como instrumentar mudanças - mas eu não recomendo-o. O href="http://en.wikipedia.org/wiki/State_pattern" rel="nofollow padrão Estado é sobre a coleta de comportamentos específicos de estados distintos em classes separadas, não sobre como presente interfaces de stateful para outras classes.

Para encomendas, pensar sobre os eventos de negócios que vêm naturalmente (por exemplo, confirmação, reconhecimento, envio Aviso, envio, fatura & c) e criar uma interface explícita ao seu redor. Exatamente como você projetar a interface depende de como a lógica do aplicativo é estruturado, e como ela é usada de outras camadas. A resposta clássica é definir métodos abstratos para cada evento de negócios (por exemplo Confirmar (), Reconheça (), ShipDateChanged ()). Se você estiver usando, por exemplo, C # você pode decidir ir com eventos de entrada e saída de seus objetos Order. Ou você pode tentar experimentar alguns mistura ou combinação destes. O ponto principal é que uma interface SetOrderState () não é muito descritivo, e é susceptível de conduzir a uma aplicação desajeitada (grandes métodos em cada uma de suas aulas OrderState).

Então, novamente, um método SetState () interna para suas classes (chamados a partir de cada um de seus métodos ou eventos específicos) pode ser OK, se você não tem um monte de código em torno das diferentes mudanças de estado: mas eu não faria expor que como uma interface externa. A desvantagem é que você pode obter alguma sobreposição entre os métodos de sua interface interna IOrderState ea interface Order exposta externamente.

Este é um julgamento, mas se eu fosse você, eu iria com o seu instinto para não expor os detalhes da sua implementação Estado para os clientes. Código que usa sua classe Order deve ser legível e compreensível.

Você pode reduzir a visibilidade do método para embalar privado, por exemplo. mas no seu caso eu acho que essa é a única maneira, de outra forma a sua ter um pai classe abstrata que implementa a máquina do Estado e só tem um grupo de nextState (inputParameter) métodos que mudam o estado da ordem para o estado correspondente.

Eu não acho que há uma resposta "certa" para isso; é realmente dependente da arquitetura que você escolheu para a sua máquina de estado. Se toda a lógica para a mudança de estado foram encapsulados dentro de sua classe Order, então eu diria que seria má prática para expor um método SetState. No entanto, desde que você já colocou alguns dos lógica de determinar o estado fora da classe Order, parece apropriado (e necessário) para expor um método SetState público, ou algo similar.

Claro, a outra opção seria a de mover a lógica de determinação do Estado para a classe Order, embora, com base no que você postou, parece como se isso iria criar uma classe muito complexo.

De qualquer forma, em suma, os padrões são realmente lá apenas para ajudá-lo a arquitetar seu código, não para definir regras rígidas e rápidas. Você deve aplicar padrões para a arquitetura da maneira que funciona melhor, não arquiteto para padrões.

Eu acho que, a transição entre estados devem estar na classe.

Por exemplo, quando você executar algum tipo de ação na ordem, a ordem sabe como mover-se para outro estado. Por Exemplo:

 public void setPaid(int amount)
 {
    //Code to pay.
    this.State = new PaidState();   //State implements the IState Interface
 }

Outra alternativa poderia ser a criação de uma nova classe para Exemplo Transformer que implementa um método como este.

 public class Transformer
 {
    public static void setState(Order o, IState s)
    {
         //Change the State.
    }
 }

Ou yoy pode usar um Enum para definir os Estados lá:

 public class Transformer
 {
    public static void setState(Order o, StatesEnum s)
    {
         //Change the State.
    }
 }

Mas eu recomendo a classe para manipular seu próprio estado. Mas lembre-se a complexidade que você terá, por outro lado.

Atenciosamente!

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