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.

[sourcecode language=”java” gutter=”false”]
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
}
[/sourcecode]

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

[sourcecode language=”java” gutter=”false”]
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!";
}
}
[/sourcecode]

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

[sourcecode language=”java” gutter=”false”]
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!");
}
}
[/sourcecode]

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

[sourcecode language=”java” gutter=”false”]
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!");
}
}
[/sourcecode]

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

[sourcecode language=”java” gutter=”false”]
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());
}
}
}
[/sourcecode]

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

[sourcecode language=”java” gutter=”false”]
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!");
}
}
[/sourcecode]

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

[sourcecode language=”java” gutter=”false”]
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!");
}
}
[/sourcecode]

É 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.

Anúncios

Deixe uma resposta