🔄 Aula 02 - Reutilização de Código em Java

Disciplina: Desenvolvimento de Sistemas II
Professor: Ricardo Pires
Turma: 3Âș TĂ©cnico DS
Tema: Princípio DRY (Don’t Repeat Yourself) e Componentes Reutilizáveis


🎯 Objetivos do Projeto

Este projeto demonstra a aplicação prĂĄtica do princĂ­pio DRY atravĂ©s da implementação de validadores centralizados para Email e CPF. Os alunos aprenderĂŁo a identificar e eliminar duplicação de cĂłdigo, criando componentes reutilizĂĄveis que aumentam a consistĂȘncia e reduzem o custo de manutenção.

CompetĂȘncias Desenvolvidas:

  • ✅ Identificar violaçÔes do princĂ­pio DRY em cĂłdigo existente
  • ✅ Extrair componentes reutilizĂĄveis seguindo boas prĂĄticas
  • ✅ Implementar validadores robustos com tratamento de edge cases
  • ✅ Criar testes parametrizados para validação de mĂșltiplos cenĂĄrios
  • ✅ Aplicar fail-fast design com mensagens de erro Ășteis

đŸ—ïž Estrutura do Projeto

projeto-pratico/
├── src/
│   ├── main/java/br/edu/etec/dsii/aula02/
│   │   ├── exemplos/
│   │   │   ├── ExemploAntes.java              # ❌ Código com duplicação
│   │   │   └── ExemploDepois.java             # ✅ Código DRY refatorado
│   │   ├── validators/
│   │   │   ├── EmailValidator.java            # 📧 Validador de email
│   │   │   └── CpfValidator.java              # 🆔 Validador de CPF
│   │   └── exercicios/
│   │       └── GabaritoExercicios.java        # 📝 SoluçÔes dos exercĂ­cios
│   └── test/java/br/edu/etec/dsii/aula02/
│       ├── validators/
│       │   ├── EmailValidatorTest.java        # đŸ§Ș Testes do EmailValidator
│       │   └── CpfValidatorTest.java          # đŸ§Ș Testes do CpfValidator
│       └── exemplos/
│           └── ExemploDepoisTest.java         # đŸ§Ș Testes do cĂłdigo refatorado
├── pom.xml                                    # 📩 Configuração Maven
├── checkstyle.xml                             # 💎 Regras qualidade código
└── README.md                                  # 📖 Este arquivo

🚀 Como Executar

Pré-requisitos

  • Java 21 ou superior
  • Maven 3.9+
  • IDE com suporte a Java (VS Code, IntelliJ, Eclipse)

1. Clonar/Baixar o Projeto

# Se usando Git
git clone https://github.com/etec-ds/aula02-reutilizacao-codigo.git
cd aula02-reutilizacao-codigo
 
# Ou baixar e descompactar o arquivo ZIP

2. Compilar e Testar

# Compilar projeto
mvn compile
 
# Executar todos os testes
mvn test
 
# Executar com relatĂłrios de cobertura
mvn clean verify
 
# Executar apenas em modo aula (mais simples)
mvn test -Paula

3. Executar Exemplos Individuais

# Exemplo com duplicação (comportamento inconsistente)
mvn exec:java -Dexec.mainClass="br.edu.etec.dsii.aula02.exemplos.ExemploAntes"
 
# Exemplo refatorado (comportamento consistente)
mvn exec:java -Dexec.mainClass="br.edu.etec.dsii.aula02.exemplos.ExemploDepois"
 
# Gabarito dos exercĂ­cios
mvn exec:java -Dexec.mainClass="br.edu.etec.dsii.aula02.exercicios.GabaritoExercicios"

📊 DemonstraçÔes PrĂĄticas

1. Problema: CĂłdigo Duplicado

// ❌ ANTES: Validação duplicada em mĂșltiplos locais
public class CadastroUsuario {
    public void cadastrar(String email) {
        if (email == null || email.trim().isEmpty()) {
            throw new IllegalArgumentException("Email obrigatĂłrio");
        }
        if (!email.contains("@")) {
            throw new IllegalArgumentException("Email invĂĄlido");
        }
        // ... lĂłgica do cadastro
    }
}
 
public class LoginController {
    public void autenticar(String email) {
        // DUPLICAÇÃO: mesma validação, implementação diferente!
        if (email.isEmpty()) {  // 🐛 BUG: não trata null!
            throw new IllegalArgumentException("Email obrigatĂłrio");
        }
        if (!email.matches(".*@.*")) {  // 🐛 BUG: regex diferente!
            throw new IllegalArgumentException("Email invĂĄlido");
        }
        // ... lógica da autenticação
    }
}

Resultado: InconsistĂȘncia comportamental → bugs em produção!

2. Solução: Componente Centralizado

// ✅ DEPOIS: Validação centralizada e consistente
public class EmailValidator {
    private static final Pattern EMAIL_PATTERN = Pattern.compile(
        "^[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*" +
        "@(?:[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?\\.)+[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$"
    );
 
    public static boolean isValid(String email) {
        return !StringUtils.isBlank(email) &&
               email.length() <= 254 &&
               EMAIL_PATTERN.matcher(email.trim().toLowerCase()).matches();
    }
 
    public static void validate(String email) {
        if (!isValid(email)) {
            throw new IllegalArgumentException("Email invĂĄlido: " + email);
        }
    }
}
 
// Uso consistente em todo o sistema
public class CadastroUsuario {
    public void cadastrar(String email) {
        EmailValidator.validate(email);  // Uma linha, comportamento garantido
        // ... lĂłgica do cadastro
    }
}

Resultado: Uma decisão → um local → comportamento consistente!


đŸ§Ș Casos de Teste Importantes

EmailValidator

@ParameterizedTest
@ValueSource(strings = {
    // Casos vĂĄlidos
    "user@example.com",           // BĂĄsico
    "test.email@domain.co.uk",    // SubdomĂ­nios
    "user+tag@example.org",       // Plus addressing
    "firstname.lastname@company.travel"  // TLD longo
})
void shouldAcceptValidEmails(String email) {
    assertTrue(EmailValidator.isValid(email));
}
 
@ParameterizedTest
@ValueSource(strings = {
    // Casos invĂĄlidos
    "",                           // Vazio
    "plainaddress",              // Sem @
    "@missinglocal.com",         // Sem parte local
    "missing@.com",              // DomĂ­nio invĂĄlido
    "toolong" + "x".repeat(250) + "@example.com"  // Muito longo
})
void shouldRejectInvalidEmails(String email) {
    assertFalse(EmailValidator.isValid(email));
}

CpfValidator

@ParameterizedTest
@ValueSource(strings = {
    "11144477735",     // Formato bĂĄsico
    "111.444.777-35",  // Com mĂĄscara
    "000.000.001-91"   // Caso extremo vĂĄlido
})
void shouldAcceptValidCpfs(String cpf) {
    assertTrue(CpfValidator.isValid(cpf));
}
 
@ParameterizedTest
@ValueSource(strings = {
    "11111111111",     // SequĂȘncia invĂĄlida
    "123.456.789-00",  // Check digits errados
    "123",             // Muito curto
    "abc.def.ghi-jk"   // Não numérico
})
void shouldRejectInvalidCpfs(String cpf) {
    assertFalse(CpfValidator.isValid(cpf));
}

🎯 Exercícios Práticos

ExercĂ­cio 1: Identificação de Duplicação ⏱ 10 min

Analise a classe ExemploAntes.java e identifique:

  1. Quantos locais diferentes fazem validação de email?
  2. Quais diferenças existem entre as implementaçÔes?
  3. Que bugs podem ocorrer devido a essas diferenças?

ExercĂ­cio 2: Refatoração DRY ⏱ 15 min

Refatore o cĂłdigo do ExemploAntes.java:

  1. Extraia um método validarEmail() centralizado
  2. Substitua todas as validaçÔes duplicadas
  3. Execute os testes para verificar se funciona

ExercĂ­cio 3: Implementação de Validador ⏱ 20 min

Implemente um PhoneValidator seguindo os padrÔes:

public class PhoneValidator {
    // Suporte a formatos: (11) 99999-9999, 11999999999, +5511999999999
    public static boolean isValid(String phone) { /* TODO */ }
    public static String normalize(String phone) { /* TODO */ }
    public static void validate(String phone) { /* TODO */ }
}

Critérios de Avaliação:

  • ✅ Suporte a mĂșltiplos formatos de entrada
  • ✅ Normalização para formato padrĂŁo
  • ✅ Validation com fail-fast e mensagens especĂ­ficas
  • ✅ Testes parametrizados cobrindo edge cases

📈 MĂ©tricas de Qualidade

Cobertura de Testes (Meta: 85%+)

# Gerar relatĂłrio JaCoCo
mvn jacoco:report
 
# Abrir: target/site/jacoco/index.html

AnĂĄlise EstĂĄtica (CheckStyle)

# Verificar qualidade do cĂłdigo
mvn checkstyle:check
 
# RelatĂłrio: target/site/checkstyle.html

Indicadores de Sucesso DRY

  • Zero duplicação de lĂłgica de validação
  • Comportamento consistente entre diferentes mĂłdulos
  • Testes unitĂĄrios cobrindo edge cases
  • Mensagens Ășteis em caso de falha
  • Facilidade de manutenção (mudança em 1 local apenas)

🔍 Análise Comparativa

Aspecto❌ Código Duplicado✅ Código DRY
Manutenção5 locais para alterar1 local centralizado
ConsistĂȘnciaComportamento divergenteComportamento garantido
TestingTeste de cada implementaçãoTeste do componente
DebuggingBug pode estar em qualquer lugarBug localizado
PerformanceMĂșltiplas compilaçÔes regexRegex compilado uma vez
LegibilidadeLógica espalhadaIntenção clara

🚧 Problemas Comuns e SoluçÔes

❓ “Meus testes falham com NullPointerException”

Solução: Verifique se estå tratando casos null nos validadores:

public static boolean isValid(String input) {
    if (StringUtils.isBlank(input)) {  // Trata null, empty e whitespace
        return false;
    }
    // ... resto da validação
}

❓ “Checkstyle reclama de ‘classes utilitárias’”

Solução: Adicione construtor privado:

public class EmailValidator {
    private EmailValidator() {
        throw new IllegalStateException("Utility class");
    }
    // ... métodos eståticos
}

❓ “Como testar se minha refatoração não quebrou nada?”

Solução: Compare comportamento antes/depois:

@Test
void shouldBehaveConsistentlyAfterRefactoring() {
    String[] testCases = {"valid@example.com", "invalid", null, ""};
 
    for (String email : testCases) {
        boolean oldResult = exemploAntes.validar(email);
        boolean newResult = EmailValidator.isValid(email);
        assertEquals(oldResult, newResult, "Comportamento mudou para: " + email);
    }
}

🔗 ReferĂȘncias e Material Extra

📚 Leitura Complementar

đŸ› ïž Ferramentas Úteis

đŸŽ„ DemonstraçÔes em VĂ­deo

  • Como refatorar cĂłdigo duplicado (15 min)
  • Implementando validadores robustos (12 min)
  • Debug de inconsistĂȘncias comportamentais (8 min)

📞 Suporte e DĂșvidas

Professor: Ricardo Pires
Email: ricardo.pires@etec.sp.gov.br
HorĂĄrio de atendimento: Seg-Sex, 18h30-21h30

❓ FAQ Rápido

  • P: Como saber se estou aplicando DRY corretamente?
    R: Se vocĂȘ precisa mudar a mesma lĂłgica em mĂșltiplos arquivos, ainda hĂĄ duplicação.

  • P: Todo cĂłdigo repetido deve ser extraĂ­do?
    R: NĂŁo! Se sĂŁo conceitos diferentes que por coincidĂȘncia sĂŁo iguais hoje, deixe separado.

  • P: Como equilibrar DRY com flexibilidade?
    R: Centralize regras de negócio, mantenha configuraçÔes flexíveis.


🏆 CritĂ©rios de Avaliação Final

CompetĂȘnciaPesoDescrição
Identificação25%Reconhece duplicação e seus impactos
Refatoração35%Extrai componentes reutilizåveis corretamente
Qualidade25%Aplica boas prĂĄticas (testes, fail-fast, etc.)
Aplicação15%Generaliza conceito para outros contextos

Entrega:

  • CĂłdigo refatorado funcionando
  • Testes passando (85%+ cobertura)
  • Documentação dos components criados
  • ReflexĂŁo sobre benefĂ­cios observados

💡 Lembre-se: DRY nĂŁo Ă© sobre evitar digitar cĂłdigo duas vezes. É sobre garantir que cada decisĂŁo seja tomada uma Ășnica vez no sistema, sendo aplicada consistentemente em todos os locais relevantes.

0 items neste arquivo.