Como fugir dos If/switch em fluxos de aprovação?

38 views
Skip to first unread message

Pedro Azin

unread,
Jan 12, 2017, 9:20:40 AM1/12/17
to CEJUG
Bom dia galera...

O meu cenário e o seguinte ... Tenho uma avaliação de um cadastro e um esta classe contém um status(Enum) de avaliação,  exemplo : 

EM_ELABORACAO,
APROVADO , 
REPROVADO , 
....

Qual e o problema... Dado um status execute um fluxo. Na maneira  tradicional criamos uma classe de serviço e avaliamos via estrutura de decisão a ação a ser executada.Sendo que este tipo de cenário criamos uma classe pouco coesa , pois esta irá crescer para sempre , a medida que novos status vão surgindo, vamos ter que acrescentar mais condições  e pior nos preocuparmos com a propagação que esta alteração pode gerar. 

Bem a solução que eu andei pensando e a seguinte...

Criar uma interface Fluxo com um método executar.
Criar as classes com implementação do fluxo.
Fazer com que meu Enum receba uma Classe que implemente a interface Fluxo

ex :  EM_ELABORACAO(new FLuxoEmElaboracao());

A medida que for necessário chamo : entidade.getStatus().getFluxo().executar().... (Ou para facilitar  entidade.chamaFluxo() encapsulando o método anterior...)

Qual e o problema que eu vejo na minha abordagem ... 
Primeiro este fluxo e um pouco complexo ... envolve alterar o status de uma classe , criar mais no minimo duas classes e ainda dispara um fluxo de email.
Segundo ponto dou o new la no enum ... como e no construtor do Enum este só e chamado uma vez... 
Como eu dou um new ... perco o gerenciamento do CDI... vou perder a possibilidade de injetar outras classes...

O que vocês acham da minha abordagem ? Qual vocês utilizam ? 

Jocelio Lima

unread,
Jan 12, 2017, 10:02:04 AM1/12/17
to CEJUG
Olá pedro, isso parece uma ótima oportunidade para aplicar o Strategy pattern, o que você está fazendo já tem uma semelhança, você já deu uma olhada?

--
-- Você está inscrito na lista de discussão técnica do CEJUG. Para sair da lista de discussão, envie um email para cejug+un...@googlegroups.com.
---
Você recebeu essa mensagem porque está inscrito no grupo "CEJUG" dos Grupos do Google.
Para cancelar inscrição nesse grupo e parar de receber e-mails dele, envie um e-mail para cejug+un...@googlegroups.com.
Para postar nesse grupo, envie um e-mail para ce...@googlegroups.com.
Acesse esse grupo em https://groups.google.com/group/cejug.
Para mais opções, acesse https://groups.google.com/d/optout.

Rafael Ponte

unread,
Jan 12, 2017, 10:19:44 AM1/12/17
to ce...@googlegroups.com, javace
Opa,

Pelo que entendi do seu problema você pode resolver com os padrões State e/ou Strategy. Ambos vão te ajudar a modelar melhor seu fluxo e permitir inserir novos fluxos sem alterar a lógica principal. De qualquer forma, não conhecer os detalhes (e complexidade) do seu problema nos leva a te propor somente soluções simples, embora já possa te dar um norte.

Enfim, a idéia de remover os ifs() a cada novo fluxo é tratar um fluxo como um objeto com seus dados e comportamentos bem definidos (semelhante ao que você fez), dessa forma sempre que um novo fluxo aparecer na regra de negócio basta criar uma nova classe. Essa é vantagem da orientação a objetos!

Se possível tenha todos os fluxo cobertos por testes automatizados!

Um abraço,
--
Rafael Ponte
TriadWorks | Formação Java
http://cursos.triadworks.com.br

Rafael Ponte

unread,
Jan 12, 2017, 10:48:04 AM1/12/17
to ce...@googlegroups.com, javace
Olá,

Fiz um esboço rápido e simples de como gerenciar o fluxo de abertura e fechamento de bugs num sistema de Issue Tracker da vida. Veja que a máquina de estados é bem simples (aberto, fechado e em analise), caso seja complexo o fluxo de estados provavelmente mudaria para trazer maior flexibilidade, embora também fosse necessário tornar o código mais abstrato e complexo.

Um abraço,

Pedro Azin

unread,
Jan 12, 2017, 11:08:04 AM1/12/17
to CEJUG, jav...@googlegroups.com
Bacana a dica de vocês , ja tinha lido sobre os padrões mencionados.

Refael sobre o teste....

De fato foi a motivação de ter começado a refatorar esta classe. Comecei a seguir abordagem do TDD, já que essa era uma funcionalidade um pouco mais complexa e eu ainda não tinha total domínio do escopo.Teste sem duvidas e algo bem importante nos projetos , nos fazem refatorar e pensar melhor no design das nossas classes e algumas vezes até nos questionar sobre o  nosso domínio sobre OO.
Perceba que esse tipo de cenário e bem comum nas aplicações... fluxos de aprovação. E por várias vezes a unica solução que pensamos e escrever um montes de IFS... aumenta a complexidade  e diminuindo cada vez mais os nossos testes.

Isto até e assunto para outro post ... Como anda seu conhecimento sobre OO ?  Por que ainda hoje e bem comum aplicações com a seguinte abordagem ... 

Uma classe Entidade ->  Uma classe  Negocio -> As vezes Classe DAO/REPOSITORY/ANY -> Controller/Managed Bean...

Arquiteturas nessas abordagem tendem a ter classes de Negócios inchadas com regra de todo jeito e dificílimas de serem testadas..



Rafael Uchôa

unread,
Jan 12, 2017, 12:23:35 PM1/12/17
to CEJUG, javace
Eu não acho que você vai conseguir manter uma interface somente devido as ações que podem ser diferentes para cada status, mas se você conseguir, só um map não resolveria ?

Map<Enum, FluxoDeProcesso> fluxos = ...
fluxos.put(APROVADO, new FluxoAprovado());
fluxos.put(RASCUNHO, new FluxoRascunho());

fluxos.get(documento.getStatus()).execute();


Rafael Uchôa
www.naskar.com.br



--
-- Você está inscrito na lista de discussão técnica do CEJUG. Para sair da lista de discussão, envie um email para cejug+unsubscribe@googlegroups.com.

---
Você recebeu essa mensagem porque está inscrito no grupo "CEJUG" dos Grupos do Google.
Para cancelar inscrição nesse grupo e parar de receber e-mails dele, envie um e-mail para cejug+unsubscribe@googlegroups.com.

Pedro Azin

unread,
Jan 12, 2017, 12:32:41 PM1/12/17
to java.ce, ce...@googlegroups.com
 Acho que não entendi muito bem ..^^

Qual seria a diferença desse abordagem da utilizado com Enum...

Rafael... 
Sobre o padrão state ...

Como que eu poderia fazer para esse estado seja persistente... criando uma tabela no banco para salvar esses possíveis estados e faria um relacionamento com a minha entidade.?? 
Ex ...

@Entity
class Avaliacao {

@ManyToOne 
private Fluxo fluxo;

}

Rafael Ponte

unread,
Jan 12, 2017, 12:50:02 PM1/12/17
to ce...@googlegroups.com, java.ce
Olá Pedro,

A idéia do Uchoa é ter uma Map como máquina de estado; funciona bem caso você tenha um fluxo contíno e simples, sem muitas variações.

Não entendi sua idéia de persistir estado no banco, ao menos não como um objeto completo - a verdade é que basta persistir somente o status em si ("ABERTO", "APROVADO" etc).

A lógica fica na aplicação representado por classes. Para ter uma mapeamento mais simples, você pode usar os Converters da JPA 2.1 (ou User Types do Hibernate), caso contrário basta encapsular na entidade.

Um abraço,

--
-- Você está inscrito na lista de discussão técnica do CEJUG. Para sair da lista de discussão, envie um email para cejug+un...@googlegroups.com.

---
Você recebeu essa mensagem porque está inscrito no grupo "CEJUG" dos Grupos do Google.
Para cancelar inscrição nesse grupo e parar de receber e-mails dele, envie um e-mail para cejug+un...@googlegroups.com.

Para postar nesse grupo, envie um e-mail para ce...@googlegroups.com.
Acesse esse grupo em https://groups.google.com/group/cejug.
Para mais opções, acesse https://groups.google.com/d/optout.

Rafael Uchôa

unread,
Jan 15, 2017, 10:25:38 PM1/15/17
to javace, CEJUG
Até dá pra criar fluxos maiores, a questão é só a interface de cada Fluxo/Processo, só pode ter uma:

https://gist.github.com/rafaeluchoa/ea84f4b8ddac721d64342326d657c33c

A ideia é guardar os status e suas possíveis ações num Map. Você pode criar o map de forma estática ou usando o CDI com beanManager.getBeans(Processo.class).

Nessa abordagem, a cada novo processo, você só precisa criar um nova classe de Processo e/ou adicionar mais itens nas enums Status e Acao.

Caso você queira guardar no banco, cria uma classe Workflow com as enum e a classe envolvida e carrega o Map baseado nessa classe.

@Entity
class WorkFlow {
    Status status;
    Acao acao;
    String clazzName;
}


Rafael Uchôa
www.naskar.com.br



Em 12 de janeiro de 2017 14:49, Rafael Ponte <rpo...@gmail.com> escreveu:
Olá Pedro,

A idéia do Uchoa é ter uma Map como máquina de estado; funciona bem caso você tenha um fluxo contíno e simples, sem muitas variações.

Não entendi sua idéia de persistir estado no banco, ao menos não como um objeto completo - a verdade é que basta persistir somente o status em si ("ABERTO", "APROVADO" etc).

A lógica fica na aplicação representado por classes. Para ter uma mapeamento mais simples, você pode usar os Converters da JPA 2.1 (ou User Types do Hibernate), caso contrário basta encapsular na entidade.

Um abraço,
On Thu, Jan 12, 2017 at 2:32 PM Pedro Azin <pedr...@gmail.com> wrote:
 Acho que não entendi muito bem ..^^

Qual seria a diferença desse abordagem da utilizado com Enum...

Rafael... 
Sobre o padrão state ...

Como que eu poderia fazer para esse estado seja persistente... criando uma tabela no banco para salvar esses possíveis estados e faria um relacionamento com a minha entidade.?? 
Ex ...

@Entity
class Avaliacao {

@ManyToOne 
private Fluxo fluxo;

}

--
-- Você está inscrito na lista de discussão técnica do CEJUG. Para sair da lista de discussão, envie um email para cejug+unsubscribe@googlegroups.com.

---
Você recebeu essa mensagem porque está inscrito no grupo "CEJUG" dos Grupos do Google.
Para cancelar inscrição nesse grupo e parar de receber e-mails dele, envie um e-mail para cejug+unsubscribe@googlegroups.com.

Para postar nesse grupo, envie um e-mail para ce...@googlegroups.com.
Acesse esse grupo em https://groups.google.com/group/cejug.
Para mais opções, acesse https://groups.google.com/d/optout.
--
Rafael Ponte
TriadWorks | Formação Java
http://cursos.triadworks.com.br

--
Você recebeu essa mensagem porque está inscrito no grupo "java.ce" dos Grupos do Google.
Para cancelar inscrição nesse grupo e parar de receber e-mails dele, envie um e-mail para javace+unsubscribe@googlegroups.com.
Para postar nesse grupo, envie um e-mail para jav...@googlegroups.com.
Acesse esse grupo em https://groups.google.com/group/javace.

Pedro Azin

unread,
Jan 16, 2017, 6:20:27 AM1/16/17
to java.ce, ce...@googlegroups.com
 Bom dia Rafael obrigado pela resposta, vou conferir.

Agora fazer uma pergunta fugindo um pouco do assunto. Você sabe me dizer se abordagem abaixo pode ter algum problema ? 

enum Flxuo{
   APROVAR(new RegraUm) ,  REPROVAR(new RegraDois)
}

A duvida e a seguinte , quera passar no construtor do meu enum uma instancia de uma regra especifica, como o enum só carrega o construtor  uma unica vez , vou ter problema de uma instancia unica das minhas regra.Por exemplo se nessa classe Regra tiver um método que recebe uma instancia qualquer  por parâmetro e a manipula... Vou ter problema com acesso concorrente? Ou a JVM  resolve o problema?

Rafael Ponte

unread,
Jan 16, 2017, 6:40:05 AM1/16/17
to ce...@googlegroups.com, java.ce
Oi Pedro,

Sua abordagem não tem problemas se ela atende seu workflow, além do que esta á uma excelente forma OO de trabalhar com enums.

Sobre concorrência, você não terá problemas enquanto trabalhar apenas com variáveis de escopo de método, ou seja, evite atributos de instância ou static.

Dependendo do caso, uma boa abordagem poderia ser passar um Contexto pra cada regra, assim facilitaria seu trabalho:

class RegraUm implements Regra {

        public void executa(RegraContexto contexto) {
            // trabalha com parametros do contexto
        }
}

No caso acima eu criei uma classe RegraContexto, mas pode ser o bom e velho Map se você achar mais conveniente.

Um abraço,


--
-- Você está inscrito na lista de discussão técnica do CEJUG. Para sair da lista de discussão, envie um email para cejug+un...@googlegroups.com.

---
Você recebeu essa mensagem porque está inscrito no grupo "CEJUG" dos Grupos do Google.
Para cancelar inscrição nesse grupo e parar de receber e-mails dele, envie um e-mail para cejug+un...@googlegroups.com.

Para postar nesse grupo, envie um e-mail para ce...@googlegroups.com.
Acesse esse grupo em https://groups.google.com/group/cejug.
Para mais opções, acesse https://groups.google.com/d/optout.

Rafael Ponte

unread,
Jan 16, 2017, 10:00:54 AM1/16/17
to ce...@googlegroups.com, java.ce
Opa,

acabei esquecendo de passar o link sobre como aproveitar melhor as enums no seu código :-)

Pedro Azin

unread,
Jan 16, 2017, 11:02:05 AM1/16/17
to java.ce, ce...@googlegroups.com
Opa Rafael,

Muito massa o post! Realmente a minha ideia foi aproveitar o enum para aplicar o padrão de projeto stratagy/state,  e com isso retirar o s ifs do codigo e torna-ló mais coeso. Parabéns o post  de grande valia para o mundo Java!
Reply all
Reply to author
Forward
0 new messages