Clean Code e Aplicação em Java com Reflections e Anotações
Introdução
Este tutorial apresenta os conceitos dos primeiros capítulos do livro "Clean Code" de Robert C. Martin e demonstra como aplicá-los em Java, utilizando reflexões e anotações para simplificar um código grande de uma implementação pura de JDBC. Usaremos uma classe de CRUD chamada ProdutoRepository
e criaremos uma anotação para facilitar a execução de queries.
Capítulo 1: Clean Code
O primeiro capítulo de "Clean Code" aborda a importância de escrever um código limpo, que é fácil de ler, entender e manter. Robert C. Martin destaca que um código limpo é aquele que se parece com uma leitura natural, tem uma estrutura clara e é escrito de maneira que outros desenvolvedores possam compreendê-lo facilmente.
Princípios de Código Limpo:
Legibilidade: O código deve ser fácil de ler.
Simplicidade: Mantenha o código o mais simples possível.
Clareza: Evite ambiguidades no código.
Eliminação de Redundância: Evite duplicação de código.
Capítulo 2: Significância dos Nomes
Neste capítulo, é enfatizada a importância de escolher nomes significativos para variáveis, funções, classes e outros elementos do código. Nomes bem escolhidos podem tornar o código muito mais legível e compreensível.
Dicas para Nomes Significativos:
Use Nomes Descritivos: Nomes devem descrever claramente a intenção.
Evite Abreviações: Use palavras completas para maior clareza.
Consistência: Use um padrão de nomenclatura consistente em todo o código.
Capítulo 3: Funções
Funções devem ser pequenas e ter uma única responsabilidade. Este capítulo detalha como escrever funções que seguem esses princípios.
Boas Práticas para Funções:
Pequenas e Concisas: Funções devem ser curtas e fazer apenas uma coisa.
Nome Descritivo: O nome da função deve descrever claramente o que ela faz.
Evitar Efeitos Colaterais: Funções não devem ter efeitos colaterais inesperados.
Reflexões
A reflexão em Java permite que um programa inspecione e modifique seu comportamento em tempo de execução. Isso é útil para criar códigos mais flexíveis e reutilizáveis.
Anotações
Anotações são usadas para fornecer metadados sobre o programa. Elas podem ser processadas em tempo de compilação ou em tempo de execução.
Comparação de Código: Antes e Depois
Código Sem Clean Code
Vamos começar com uma implementação típica de CRUD em JDBC sem aplicar os princípios de Clean Code.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class ProdutoRepository {
private static final String URL = "jdbc:mysql://localhost:3306/seu_banco";
private static final String USER = "seu_usuario";
private static final String PASSWORD = "sua_senha";
public Produto findById(int id) {
Produto produto = null;
try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD)) {
String query = "SELECT * FROM produtos WHERE id = ?";
try (PreparedStatement statement = connection.prepareStatement(query)) {
statement.setInt(1, id);
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
produto = new Produto(resultSet.getInt("id"), resultSet.getString("nome"), resultSet.getDouble("preco"));
}
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return produto;
}
public void save(String nome, double preco) {
try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD)) {
String query = "INSERT INTO produtos (nome, preco) VALUES (?, ?)";
try (PreparedStatement statement = connection.prepareStatement(query)) {
statement.setString(1, nome);
statement.setDouble(2, preco);
statement.executeUpdate();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public void update(int id, String nome, double preco) {
try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD)) {
String query = "UPDATE produtos SET nome = ?, preco = ? WHERE id = ?";
try (PreparedStatement statement = connection.prepareStatement(query)) {
statement.setString(1, nome);
statement.setDouble(2, preco);
statement.setInt(3, id);
statement.executeUpdate();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public void delete(int id) {
try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD)) {
String query = "DELETE FROM produtos WHERE id = ?";
try (PreparedStatement statement = connection.prepareStatement(query)) {
statement.setInt(1, id);
statement.executeUpdate();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Agora vamos reescrever a classe ProdutoRepository aplicando os princípios de Clean Code e utilizando anotações para simplificar a execução das queries.
Passo 1: Criação da Anotação @Query
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Query {
String value();
}
Passo 2: refatoração do repository:
public class ProdutoRepository {
@Query("SELECT * FROM produtos WHERE id = ?")
public Produto findById(int id) {
// Implementação será simplificada pelo QueryProcessor
return null;
}
@Query("INSERT INTO produtos (nome, preco) VALUES (?, ?)")
public void save(String nome, double preco) {
// Implementação será simplificada pelo QueryProcessor
}
@Query("UPDATE produtos SET nome = ?, preco = ? WHERE id = ?")
public void update(int id, String nome, double preco) {
// Implementação será simplificada pelo QueryProcessor
}
@Query("DELETE FROM produtos WHERE id = ?")
public void delete(int id) {
// Implementação será simplificada pelo QueryProcessor
}
}
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class QueryProcessor {
private static final String URL = "jdbc:mysql://localhost:3306/seu_banco";
private static final String USER = "seu_usuario";
private static final String PASSWORD = "sua_senha";
public static void executeAnnotatedMethods(Object obj, Object... params) throws Exception {
Class<?> clazz = obj.getClass();
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(Query.class)) {
Query query = method.getAnnotation(Query.class);
executeQuery(query.value(), params);
}
}
}
private static void executeQuery(String query, Object... params) throws Exception {
try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
PreparedStatement statement = connection.prepareStatement(query)) {
for (int i = 0; i < params.length; i++) {
statement.setObject(i + 1, params[i]);
}
if (query.trim().toUpperCase().startsWith("SELECT")) {
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
// Processar resultado
}
}
} else {
statement.executeUpdate();
}
}
}
public static void main(String[] args) throws Exception {
ProdutoRepository repository = new ProdutoRepository();
executeAnnotatedMethods(repository, 1); // Exemplo para encontrar produto por ID
}
}
Comparação e Benefícios
Antes (Sem Clean Code)
Código repetitivo: Cada método do repositório contém código similar para abrir conexões, preparar statements e executar queries. Baixa reutilização: Código para manipulação de banco de dados não é reutilizável. Difícil manutenção: Mudanças na lógica de conexão ou tratamento de exceções requerem alterações em múltiplos lugares.
Redução de Redundância: A lógica para abrir conexões e executar queries é centralizada no QueryProcessor. Aumento da Legibilidade: Uso de anotações @Query deixa claro o propósito de cada método sem se preocupar.
Last modified: 22 July 2024