image

Acesse bootcamps ilimitados e +650 cursos pra sempre

60
%OFF
Article image
Gustavo Alves
Gustavo Alves30/03/2023 18:51
Compartilhe

Código limpo e Extensível

  • #PHP
  • #Java

Bom, temos uma API de cadastramento de pessoas para uma locadora de carros. Para uma pessoa realizar seu cadastro, ela precisa informar sua data de nascimento, seu nome, seu CPF, o número de cartão de crédito e um endereço.


Temos a classe ClientService, ela tem um método saveClient, responsável por validar alguns dados como, o CPF e o endereço, e persiste o cliente na base de dados.


@Service
public class ClientService {

 @Autowired
 private ClientRepository repository;

 @Autowired
 private AddressInfra addressInfra;

 public Client saveClient(Client client) {

     if (repository.existsByCpf(client.getCpf())) {

         throw new CpfExistException("Cpf already exist");

     }

     if (addressInfra.validationAdress(client.getAddress()) == null) {

         throw new InvalidAddressException("CEP invalid");

     }

     return repository.save(client);

 }

}


Observe que o método utiliza if’s para validar cada dado. Acho que você notou que a aplicação não exige o número da CNH do cliente, agora além da adição desse atributo na classe Client, também é necessário validar esse dado, então vamos adicionar mais um if no método saveClient. Adicionar novos if's ao método vai tornar a leitura humana e a manutenção difícil.


@Service
public class ClientService {

 @Autowired
 private ClientRepository repository;

 @Autowired
 private AddressInfra addressInfra;

 @Autowired
 private CnhInfra cnhInfra;

 public Client saveClient(Client client) {

     if (repository.existsByCpf(client.getCpf())) {
         throw new CpfExistException("Cpf already exist");
     }

     if (addressInfra.validationAdress(client.getAddress()) == null) {
         throw new InvalidAddressException("CEP invalid");
     }
    
     if (!cnhInfra.validationCNH(client.getCNH)){
         throw new InvalidCNHException("CNH invalid");
     }

     return repository.save(client);
 }
}


Para resolver esse problema, devemos aplicar o princípio da Responsabilidade Única(“Uma classe deve ter um, e somente um, motivo para mudar”), criando um método separado para lidar com as validações, chamado validations.


@Service

public class ClientService {
 @Autowired
 private ClientRepository repository;

 @Autowired
 private AddressInfra addressInfra;

 @Autowired
 private CnhInfra cnhInfra;

 public Client saveClient(Client client) {
     validations(client);

     return repository.save(client);
 }

 private void validations(Client client) {

     if (repository.existsByCpf(client.getCpf())) {

         throw new CpfExistException("Cpf already exist");
     }

     if (addressInfra.validationAdress(client.getAddress()) == null) {

         throw new InvalidAddressException("CEP invalid");
     }

     if (!cnhInfra.validationCNH(client.geetCNH)) {

         throw new InvalidCNHException("CNH invalid");
     }

 }

}


Porém, cada nova validação adicionada ainda resultará em um novo if no método validations. Para solucionar esse problema, devemos aplicar o princípio do Aberto e Fechado(“Objetos ou entidades devem estar abertos para extensão, mas fechados para modificação”), criando classes para cada validação que implementem a interface Validation e utilizem o método validator.


@Component
public interface Validation {

  void validator(Client client);
}


@Component
public class AddressValidation implements Validation {
 @Autowired
 AddressInfra addressInfra;

 @Override
 public void validator(Client client) {
     Optional.ofNullable((addressInfra.validationAdress(client.getAddress())))
            .orElseThrow(() -> new InvalidAddressException("CEP invalid"));
 }
}


@Component
public class CpfValidation implements Validation {

 @Autowired
 ClientRepository clientRepository;

 @Override
 public void validator(Client client) {
     if (clientRepository.existsByCpf(client.getCpf())){

      throw new CpfExistException("Cpf already exist");
     }
 }
}


Com a ajuda do Framework Spring, podemos injetar uma lista de Validation na classe ClientService , e utilizar o método forEach para executar a validação de cada classe que está implementando a interface Validation.


@Service
public class ClientService {

 @Autowired
 private ClientRepository repository;

 @Autowired
 List<Validation> validations;

 public ClientDto saveClient(Client client){
     validations.forEach(validation -> validation.validator(client));

     return repository.save(client);
 }
}


Se você não notou, acabamos de usar o Princípio da inversão da dependência, outro princípio do SOLID. Ou seja, a classe ClientService não depende e não tem relações com as classes CpfValidation e AddressValidation, a única relação da classe ClientService é com a interface Validation que é uma abstração. 


Agora temos um código extensível, limpo e com as responsabilidades bem distribuídas. Vale a pena lembrar que não existe “Bala de prata” e sempre é bom avaliar caso-a-caso!

Compartilhe
Comentários (2)
Moacir Gonçalves
Moacir Gonçalves - 30/03/2023 20:16

Que artigo massa, parabéns! Bem objetivo!!! Obrigado!

MA

Matheus Araújo - 30/03/2023 19:40

Excelente conteúdo Gustavo, deu pra entender na prática os princípios do SOLID, valeu!!!