Você não é, você está! State

Igor Carvalho
php-brasil
Published in
3 min readAug 30, 2017

--

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.

--

--