Aplicando desenvolvimento orientado a interfaces com Java

Veja neste artigo como utilizar o modelo de desenvolvimento orientado a interfaces em Java, reduzindo o acoplamento entre os componentes e provendo um maior desempenho na aplicação.

Neste artigo será apresentado um modelo de desenvolvimento de software mais utilizado pelas Fábricas de Software. bO modelo consiste em desenvolver aplicações Java com menor acoplamento entre os componentes, provendo assim um maior desempenho na aplicação. Por Java ser uma linguagem OO (Orientada a Objetos) é preciso obedecer todas as restrições desse paradigma na hora de desenvolver uma aplicação, são eles:

  1. Encapsulamento
  2. Polimorfismo
  3. Herança

Em muitos casos é comum ler citações onde desenvolvedores mais experientes preferem o desenvolvimento de software voltado à interfaces ao invés de usar herança.

“Ao modelar um software é preferível escolher Composição ao invés de herança.” (Aristofânio Garcia, (2013))

O encapsulamento aplica-se à forma de acesso aos atributos de um objeto. No caso da linguagem Java, encapsulamento pode ser definido através dos seguintes modificadores de acesso: private, protected, public ou default, cada um desses tipos de acessos acarretam em formas deferentes de encapsulamento do código, onde pode se ter:

Já o polimorfismo baseia-se na capacidade de ter várias formas de instanciar um objeto, pode-se dizer que ao ter sobrecarga do construtor de uma determinada classe já se faz possível trabalhar de forma polimórfica.

Interligando os conceitos de encapsulamento e polimorfismo, podemos encontrar o conceito de abstração, onde em linguagens orientada a objetos podemos utilizá-la a partir de um contrato, em outras palavras utilizando interfaces para prover comunicação entre diferentes partes da aplicação. Dessa forma é possível diminuir o acoplamento, aumentar a coesão e tornar o código mais legível e extensível.

Colocando em Prática

Após identificar e separar os módulos que farão parte do domínio da aplicação ficará mais fácil identificar os pontos onde farão conexão com o a visão da aplicação, domínio e o banco de dados.

Para melhorar a coesão do código, é uma ótima prática dividir classes em pacotes, de forma que se possa separar as responsabilidades.

Figura 1. Estruturando o projeto

No pacote br.com.example.domain serão colocadas as classes java beans, para trabalhar com banco de dados objeto relacional (ORM). Como no caso do JPA, é preciso que os objetos sejam serializados.

package br.com.example.domain; import java.io.Serializable; public class Person implements Serializable { /** * */ private static final long serialVersionUID = 1L; private Integer code; private String name; private String cpf; private String rg; public Integer getCode() { return code; } public String getName() { return name; } public String getCpf() { return cpf; } public String getRg() { return rg; } public void setCode(Integer code) { this.code = code; } public void setName(String name) { this.name = name; } public void setCpf(String cpf) { this.cpf = cpf; } public void setRg(String rg) { this.rg = rg; } /*É importante adicionar os hashCode e Equals para melhorar a performance na hora de efetuar buscas no banco de dados*/ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((code == null) ? 0 : code.hashCode()); result = prime * result + ((cpf == null) ? 0 : cpf.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + ((rg == null) ? 0 : rg.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (code == null) { if (other.code != null) return false; } else if (!code.equals(other.code)) return false; if (cpf == null) { if (other.cpf != null) return false; } else if (!cpf.equals(other.cpf)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (rg == null) { if (other.rg != null) return false; } else if (!rg.equals(other.rg)) return false; return true; } }
Listagem 1. Classes Bean

Já no pacote br.com.example.application serão representadas as interfaces que representarão o domínio da aplicação, dessa forma é possível implementar códigos inerentes ao domínio do negócio, tornando-o independente de tipo de banco de dados, separando assim domínio da aplicação do banco de dados.

package br.com.example.application; import br.com.example.domain.Person; public interface PersonApplication { public void persist(Person person); public void update (Person person); public void delete(Person person); public Person findByCode(Integer code); }
Listagem 2. Pacote Application

O pacote br.com.example.application.impl contém as classes que implementam as interfaces inerentes ao domínio da aplicação, sua função basicamente é realizar a conexão entre o banco de dados e a aplicação.

package br.com.example.application.impl; import br.com.example.application.PersonApplication; import br.com.example.domain.Person; import br.com.example.repository.PersonRepository; public class PersonApplicationImpl implements PersonApplication { private PersonRepository personInf; @Override public void persist(Person person) { personInf.persist(person); } @Override public void update(Person person) { personInf.update(person); } @Override public void delete(Person person) { personInf.delete(person); } @Override public Person findByCode(Integer code) { return personInf.findByCode(code); } }
Listagem 3. Classes do application fazendo chamadas ao repositório através da interface

Seguindo essa mesma ideia, os pacotes br.com.example.repository e br.com.example.repository.impl contém as interfaces e as classes que as implementam formando a camada que estabelece a conexão com o banco de dados e efetua a lógica de acesso aos dados do banco.

package br.com.example.repository.impl; import br.com.example.domain.Person; import br.com.example.repository.PersonRepository; public class PersonRepositoryImpl implements PersonRepository { @Override public void persist(Person person) { /* * Aqui toda a lógica para salvar a entidade Person, independente de * qual for o banco de dados! */ } @Override public void update(Person person) { // TODO Auto-generated method stub } @Override public void delete(Person person) { // TODO Auto-generated method stub } @Override public Person findByCode(Integer code) { // TODO Auto-generated method stub return null; } }
Listagem 4. Interface Repository

Conclusão

Seguindo esse padrão, as aplicações tornam-se mais independentes e é possível alterar o banco de dados sem que haja necessidades de alterar o domínio de negócios. Em alguns casos podem ser necessárias alterações em toda a tecnologia aplicada à camada de, entretanto seguindo este padrão é possível alterar também a visão sem a necessidade de alterar o domínio do negócio, deixando o código expansível, coeso e menos acoplado.

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados