Impulsojunte-se à Impulso
Imagem preta e branca de máquinas - artigo da Impulso sobre chain of responsibility

24/10/19

5 min de leitura

Imagem preta e branca de máquinas - artigo da Impulso sobre chain of responsibility

Chain of Responsibility e Múltipla Injeção de dependências com Spring Boot

Orlando Burli JúniorOrlando Burli Júnior

Introdução

O Spring Boot é um framework para criação de serviços stand-alone, ou serverless, como é o termo usado hoje em dia. Faz parte do ecossistema Spring, que contém um grande número de bibliotecas para auxiliar os desenvolvedores a criar suas aplicações.

Neste artigo, vamos demonstrar o design pattern Chain of Responsibility e como implementá-lo com baixo acoplamento no Spring Boot.

Inversion of control (ioc) e a injeção de dependências

Atualmente, o IOC (Inversion of Control) é um padrão de projeto muito forte e muito utilizado principalmente nas aplicações Java EE. Ele tem a injeção de dependências (Dependency Injection) como uma implementação deste pattern. Nada mais é do que injetar dependências de componentes nas classes onde as declaramos, através de anotações específicas, de acordo com o framework/tecnologia utilizados.

Injeção de Dependências no Spring Boot

Para anotar uma classe no Spring Boot, são necessárias duas coisas: que as classes sejam Components (ou seus subtipos) e que seja adicionada à anotação @Autowired.

@Component
public class AlunoService {
    // ... código omitido
}
@Component
public class MatriculaService {

    // Injeção de alunoService 
    @Autowired
    private AlunoService alunoService;

    public void matricula(Aluno aluno, Curso curso) {
        // ... código omitido
    }
}

No Spring Boot, temos outros subtipos de @Component, como o @Repository e o @Service, mas estes produzem o mesmo efeito de injeção de dependências.

Design Pattern “Chain of Responsibility”

Talvez você já tenha ouvido falar neste design pattern, que é em criar uma “cadeia de responsabilização” das suas regras de negócio. Elas são isoladas em classes menores e “passando a bola” para o próximo item da lista, como numa cadeia de responsabilização.

A ideia deste padrão de projeto é produzir um baixo acoplamento entre estas classes, de forma que adicionar novas regras não impacte nas já existentes.

Exemplo de aplicação

Uma forma mais tradicional de realizar esse pattern é declarar todas as classes que vão compor essas cadeias e, então, encadeá-las para as chamadas, como no exemplo abaixo:

Exemplo 1: Chain of responsibility com POJO’s

/// Classe model de aluno
public class Aluno {
 // dados omitidos
}

public interface IAlunoProcessor() {
 void setNext(IAlunoProcessor next);
 void chain(Aluno aluno);
}

public class FinanceiroService implements IAlunoProcessor {
 private IAlunoProcessor next;

 public void setNext(IAlunoProcessor next) {
  this.next = next;
 }

 public void chain(Aluno aluno) {
  // Realiza tratamentos de negócio para o financeiro
  // Depois retorna o aluno para a próxima cadeia
  if (this.next != null) next.chain(aluno);
 }
}

public class BibliotecaService implements IAlunoProcessor {
 private IAlunoProcessor next;

 public void setNext(IAlunoProcessor next) {
  this.next = next;
 }

 public void chain(Aluno aluno) {
  // Realiza tratamentos de negócio para a biblioteca
  // Depois retorna o aluno para a próxima cadeia
  if (this.next != null) next.chain(aluno);
 }
}

public class CantinaService implements IAlunoProcessor {
 private IAlunoProcessor next;

 public void setNext(IAlunoProcessor next) {
  this.next = next;
 }

 public Aluno chain(Aluno aluno) {
  // Realiza tratamentos de negócio para a cantina
  // Depois retorna o aluno para a próxima cadeia
  if (this.next != null) next.chain(aluno);
 }
}

// Classe de negócio de matrícula
public class MatriculaService {

 public void matricular(Aluno aluno) {
  // Criação dos serviços
  FinanceiroService financeiroService = new FinanceiroService();
  BibliotecaService bibliotecaService = new BibliotecaService();
  CantinaService cantinaService = new CantinaService();

  // Aninhamento das cadeias
  financeiroService.setNext(bibliotecaService);
  bibliotecaService.setNext(cantinaService);

  // Chama o primeiro serviço, que dispara todas as cadeias.
  financeiroService.chain(aluno);
 }
}

Percebe-se que ainda existe um certo acoplamento, visto que a classe MatriculaService precisa ter conhecimento de todas as classes da cadeia para que a essa possa ser executada.

Spring Boot e as múltiplas Injeções

Um recurso no Spring Boot (aqui foi testado na versão 2.0) permite que injetemos diversos Componentes, ou Services, filtrando os mesmos por uma interface ou até por uma anotação específica.

Vejamos este mesmo exemplo usando Spring Boot:

// Classe model de aluno
public class Aluno {
 // dados omitidos
}

public interface IAlunoProcessor() {
 void setNext(IAlunoProcessor next);
 void chain(Aluno aluno);
}

public class FinanceiroService implements IAlunoProcessor {
 private IAlunoProcessor next;

 public void setNext(IAlunoProcessor next) {
  this.next = next;
 }

 public void chain(Aluno aluno) {
  // Realiza tratamentos de negócio para o financeiro
  // Depois retorna o aluno para a próxima cadeia
  if (this.next != null) next.chain(aluno);
 }
}

public class BibliotecaService implements IAlunoProcessor {
 private IAlunoProcessor next;

 public void setNext(IAlunoProcessor next) {
  this.next = next;
 }

 public void chain(Aluno aluno) {
  // Realiza tratamentos de negócio para a biblioteca
  // Depois retorna o aluno para a próxima cadeia
  if (this.next != null) next.chain(aluno);
 }
}

public class CantinaService implements IAlunoProcessor {
 private IAlunoProcessor next;

 public void setNext(IAlunoProcessor next) {
  this.next = next;
 }

 public Aluno chain(Aluno aluno) {
  // Realiza tratamentos de negócio para a cantina
  // Depois retorna o aluno para a próxima cadeia
  if (this.next != null) next.chain(aluno);
 }
}

// Classe de negócio de matrícula
public class MatriculaService {

 public void matricular(Aluno aluno) {
  // Criação dos serviços
  FinanceiroService financeiroService = new FinanceiroService();
  BibliotecaService bibliotecaService = new BibliotecaService();
  CantinaService cantinaService = new CantinaService();

  // Aninhamento das cadeias
  financeiroService.setNext(bibliotecaService);
  bibliotecaService.setNext(cantinaService);

  // Chama o primeiro serviço, que dispara todas as cadeias.
  financeiroService.chain(aluno);
 }
} // Classe model de aluno
public class Aluno {
 // dados omitidos
}

public interface IAlunoProcessor() {
 Integer getPriority();
 void chain(Aluno aluno);
}

@Component
public class FinanceiroService implements IAlunoProcessor {

 public Integer getPriority() {
  return 1;
 }

 public void chain(Aluno aluno) {
  // realiza tratamentos de negócio para o financeiro...
 }
}

@Component
public class BibliotecaService implements IAlunoProcessor {
 public Integer getPriority() {
  return 2;
 }

 public void chain(Aluno aluno) {
  // realiza tratamentos de negócio para a biblioteca...
 }
}

@Component
public class CantinaService implements IAlunoProcessor {
 public Integer getPriority() {
  return 3;
 }

 public Aluno chain(Aluno aluno) {
  // realiza tratamentos de negócio para a cantina...
 }
}

// Classe de negócio de matrícula
@Component
public class MatriculaService {

 @Autowired
 private List < IAlunoProcessor > processors;

 public void matricular(Aluno aluno) {
  // Aqui já temos todas as classes que extendem de IAlunoProcessor injetadas
  processors
   // Transformamos a mesma em stream
   .stream()
   // Ordenamos pelo método getPriority() (Esta parte é opcional)
   .sorted((a, b) -> a.getPriority().compareTo(b.getPriority()))
   // E executamos cada uma das regras.
   .forEach(next -> next.chain(aluno));
 }
}

Perceba que as classes FinanceiroService, BibliotecaService, e CantinaService, não têm mais vínculo entre si, e a classe MatriculaService não tem qualquer dependência com as três anteriores, ou seja, aplicamos baixo acoplamento. Para adicionar novas classes, basta implementar a interface IAlunoProcessor e definir a prioridade, que a mesma será automaticamente executada pelo serviço de matrícula.

Conclusão

O Spring é um framework bem poderoso para criação de aplicações java, principalmente para microsserviços e aplicações stand-alone. O recurso de múltiplas injeções e o pattern chain of responsibility é um, entre muitos, que permite criar regras de negócio claras e isoladas entre si. O que permite um código limpo e de fácil compreensão a todas as pessoas desenvolvedoras.

Então, quer saber mais sobre frameworks? Participe do nosso grupo no Discord

Nós usamos cookies para melhorar sua experiência no site. Ao aceitar, você concorda com nossa Política de Privacidade

Assine nossa newsletter

Toda semana uma News com oportunidades de trabalho, conteúdos selecionados, eventos importantes e novidades sobre o Mundo da Tecnologia.

Pronto, em breve você vai receber novidades 👍