State Pattern

O State é um padrão  que utiliza composição de classes para representar a variação de comportamento de uma determinada entidade durante a execução de um sistema. Segundo Guerra, essas classes seguem uma abstração comum (interface ou superclasse) e os métodos de negócio delegam para esse objeto a responsabilidade de controlar o estado. Simplificando a manutenção e eliminando condicionais no código.

Um exemplo de aplicação do State Pattern é o seguinte: recursos são solicitados para projetos por clientes e aprovados ou recusados por analistas e gestores da empresa. A Figura 1 exibe o diagrama representando o fluxo de estados para a solicitação.

diagramadeestado
Figura 1. Diagrama de Estados para um Recurso Solicitado

A classe SolicitacaoStatus abstrai um estado da entidade Solicitacao e define a interface dos métodos que o alteram. Ao trocar a instância utilizada na propriedade status, consequentemente altera-se o comportamento da entidade.

public abstract class SolicitacaoStatus implements Serializable {

     protected Solicitacao solicitacao;

     public abstract void solicitar() throws SolicitacaoException;

     public abstract void aprovar() throws SolicitacaoException;

     public abstract void recusar() throws SolicitacaoException;

     public abstract void analisar() throws SolicitacaoException;

     public abstract void encaminhar() throws SolicitacaoException;

     //... getters and setters
}

Na classe Solicitacao, apenas a lógica comum será mantida e o comportamento específico de cada estado estará nas subclasses de SolicitacaoStatus.

public class Solicitacao implements Serializable {

     private SolicitacaoStatus status = new SolicitacaoStatusEmElaboracao(this);

     private Usuario solicitante;

     private Usuario aprovador;

     private Date dataSolicitacao = new Date();

     //... getters and setters

     public String solicitar() throws SolicitacaoException {
        // mais ações referentes a solicitação...
        this.setSolicitante(usuario);
        this.status.setSolicitacao(this);
        this.status.solicitar();
        return "Solicitação realizada!";
     }

     public String aprovar() throws IncubadoraException {
        // mais ações referentes a aprovação...
        this.setAprovador(usuario);
        this.status.setSolicitacao(this);
        this.status.aprovar();
        return "Solicitação aprovada!";
     } 

     public String recusar() throws SolicitacaoException {
        // mais ações referentes a recusa...
        this.status.setSolicitacao(this);
        this.status.recusar();
        return "Solicitação recusada!";
     } 

     public String analisar() throws SolicitacaoException {
        // mais ações referentes a analise...
        this.status.setSolicitacao(this);
        this.status.analisar();
        return "Solicitação analisada!";
     } 

     public String encaminhar() throws SolicitacaoException {
        // mais ações referentes a encaminhamento para gestor...
        this.status.setSolicitacao(this);
        this.status.encaminhar();
        return "Solicitação encaminhada!";
     }
}

O estado inicial será uma instância para a subclasse SolicitacaoStatusEmElaboracao.

public class SolicitacaoStatusEmElaboracao extends SolicitacaoStatus {

    public SolicitacaoStatusEmElaboracao() {
        super();
    }

    public SolicitacaoStatusEmElaboracao(Solicitacao solicitacao) {
        this.solicitacao = solicitacao;
    }

    @Override
    public void solicitar() throws SolicitacaoException {
      getSolicitacao().setStatus(new SolicitacaoStatusAguardandoResposta());
    }

    @Override
    public void aprovar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido aprovar!");
    }

    @Override
    public void recusar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido recusar!");
    }

    @Override
    public void analisar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido analisar!");
    }

    @Override
    public void encaminhar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido encaminhar para o gestor!");
    }
}

Após a elaboração, o estado muda para Aguardando Resposta implementada na classe:

public class SolicitacaoStatusAguardandoResposta extends SolicitacaoStatus {

    public SolicitacaoStatusAguardandoResposta() {
        super();
    }

    public SolicitacaoStatusAguardandoResposta(Solicitacao solicitacao) {
        this.solicitacao = solicitacao;
    }

    @Override
    public void solicitar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido solicitar recurso!");
    }

    @Override
    public void aprovar() throws SolicitacaoException {
        getSolicitacao().setStatus(new SolicitacaoStatusAprovado());
    }

    @Override
    public void recusar() throws SolicitacaoException {
        getSolicitacao().setStatus(new SolicitacaoStatusRecusado());
    }

    @Override
    public void analisar() throws SolicitacaoException {
      getSolicitacao().setStatus(new SolicitacaoStatusAguardandoAnalise());
    }

    @Override
    public void encaminhar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido encaminhar para gestor!");
    }
}

Ao encaminhar solicitação para um analista, o estado muda para Aguardando Analise:

public class SolicitacaoStatusAguardandoAnalise extends SolicitacaoStatus {

    public SolicitacaoStatusAguardandoAnalise() {
        super();
    }

    public SolicitacaoStatusAguardandoAnalise(Solicitacao solicitacao) {
        this.solicitacao = solicitacao;
    }

    @Override
    public void solicitar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido solicitar recurso!");
    }

    @Override
    public void aprovar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido aprovar!");
    }

    @Override
    public void analisar() throws SolicitacaoException {
        getSolicitacao().setStatus(new SolicitacaoStatusAguardandoAnalise());
    }

    @Override
    public void recusar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido recusar!");
    }

    @Override
    public void encaminhar() throws SolicitacaoException {
        if (solicitacao.getQtdDeAnalisesEmAndamento() <= 1) {
            solicitacao.setStatus(new SolicitacaoStatusAguardandoResposta());
        }
    }
}

O estado muda para Aprovado quando algum analista ou gestor aprova a solicitação:

public class SolicitacaoStatusAprovado extends SolicitacaoStatus {

    public SolicitacaoStatusAprovado() {
        super();
    }

    public SolicitacaoStatusAprovado(Solicitacao solicitacao) {
        this.solicitacao = solicitacao;
    }

    @Override
    public void solicitar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido solicitar recurso!");
    }

    @Override
    public void aprovar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido aprovar!");
    }

    @Override
    public void recusar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido recusar!");
    }

    @Override
    public void analisar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido analisar!");
    }

    @Override
    public void encaminhar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido encaminhar para gestor!");
    }
}

O estado muda para Recusado quando algum analista ou gestor recusa a solicitação:

public class SolicitacaoStatusRecusado extends SolicitacaoStatus {

    public SolicitacaoStatusRecusado() {
        super();
    }

    public SolicitacaoStatusRecusado(Solicitacao solicitacao) {
        this.solicitacao = solicitacao;
    }

    @Override
    public void solicitar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido solicitar recurso!");
    }

    @Override
    public void aprovar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido aprovar!");
    }

    @Override
    public void recusar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido recusar!");
    }

    @Override
    public void analisar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido analisar!");
    }

    @Override
    public void encaminhar() throws SolicitacaoException {
        throw new SolicitacaoException("Não é permitido encaminhar para gestor!");
    }
}

É convenção criar a classe que tem o estado quanto os estados no mesmo pacote, assim utilizando o modificador de acesso dos atributos como protected, permite-se que classes vizinhas enxerguem e manipulem seus atributos.

O conceito do que significa um estado para uma determinada entidade do software pode começar a ficar explícito somente no momento da codificação. A repetição de condicionais similares em diversos pontos da mesma classe pode ser sinal de que seria adequada a refatoração do código em direção ao padrão State.” (GUERRA, 2013)

Segundo Aniche, o controle das possíveis transições são vários e complexos em uma máquina de estados, fazendo com que a implementação não seja simples. O State auxilia a manter o controle dos estados simples e organizados através da criação de classes que representem cada estado e saiba controlar as transições.

Referências e Links

ANICHE, Maurício. Design Patterns Java I: Boas práticas de programação. Curso online na plataforma Alura. Disponível em: https://www.alura.com.br/curso-online-design-patterns.

GUERRA, Eduardo. Design Patterns com Java: Projeto orientado a objetos guiado por padrões. Disponível em: https://www.casadocodigo.com.br/products/livro-design-patterns.

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s