Você não é, você está! State
PS.: este artigo será dividido em duas partes
Uma outra forma de definir um if de forma elegante é usando o State Pattern. Este padrão difere do Strategy na forma como “produz” esse if: o objetivo do strategy é alternar algoritmo poliformicamente, enquanto o state o faz de forma a introduzir uma regra nessa troca.
Vamos dar uma olhada na definição do padrão:
Permite a um objeto alterar seu comportamento quando o seu estado interno muda. O objeto parecerá ter mudado sua classe.
Vamos ver sua aplicabilidade:
1- o comportamento de um objeto depende do seu estado e ele pode mudar seu
comportamento em tempo de execução, dependendo desse estado;
2- operações têm comandos condicionais grandes, de várias alternativas, que
dependem do estado do objeto. Esse estado é normalmente representado por
uma ou mais constantes enumeradas.
Isto é ideal para definir certos workflows. Vamos a um exemplo?
Imaginemos que temos um processo seletivo no qual um operador do sistema avalia determinado documento de um candidato específico. O documento tem um workflow específico: quando ele é cadastrado no sistema ele entra com o status aguardando (waiting), e quando o operador o baixa para análise, o sistema indica que ele está sendo analisado (in analisis — regra de negócio). Posteriormente ele pode ser negado (denied) ou aprovado (approved), e numa eventual conversa entre candidato-instituição o documento negado pode se transformar em aprovado:
Bom, para começar, vamos definir os estados do documento, vamos fazer isso através de uma interface:
Agora vamos definir um comportamento padrão para iniciar a definição do workflow, através de uma classe abstrata:
Mas para uma classe ser abstrata, ela precisa ter ao menos um método abstrato:
Agora vamos definir o workflow, começando pelo “aguardando”:
Perceba que WaitingState herda de AbstractDocumentState, e ao sobrescrever o método inAnalisis eu estou permitindo meu documento transitar apenas para este estado (lembra que o padrão é lançar uma exception?).
O próximo estado é o InAnalisis:
Assim como o WaitingState, o InAnalisisState só permite a transição do estado para aprovado ou negado. Se por acaso tentar voltar para “aguardando” (waiting) será lançada uma exception.
Agora o DeniedState:
Note que este estado só permite transitar para “aprovado” (approved). Qualquer tentativa de voltar para “aguardando” ou “em análise” também lançará exceptions.
E por último o ApprovedState:
Este estado não permite voltar para nenhum estado anterior.
Como se dá a mágica?
Aqui está nosso documento, com métodos para transitar entre estados. Vamos aos testes?
Quem prefere fazer na mão?
Quem prefere usar ferramenta de teste?
O mais interessante de usar ferramentas de testes é testar as exceptions previstas. ;)
Até a parte 2 do artigo.
PS2.: este é o repo.