đ 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 ZIP2. 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 -Paula3. 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:
- Quantos locais diferentes fazem validação de email?
- Quais diferenças existem entre as implementaçÔes?
- Que bugs podem ocorrer devido a essas diferenças?
ExercĂcio 2: Refatoração DRY â±ïž 15 min
Refatore o cĂłdigo do ExemploAntes.java:
- Extraia um método
validarEmail()centralizado - Substitua todas as validaçÔes duplicadas
- 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.htmlAnĂĄlise EstĂĄtica (CheckStyle)
# Verificar qualidade do cĂłdigo
mvn checkstyle:check
# RelatĂłrio: target/site/checkstyle.htmlIndicadores 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ção | 5 locais para alterar | 1 local centralizado |
| ConsistĂȘncia | Comportamento divergente | Comportamento garantido |
| Testing | Teste de cada implementação | Teste do componente |
| Debugging | Bug pode estar em qualquer lugar | Bug localizado |
| Performance | MĂșltiplas compilaçÔes regex | Regex compilado uma vez |
| Legibilidade | Lógica espalhada | Intençã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
- The Pragmatic Programmer - DRY Principle
- Martin Fowler - Refactoring: Extract Method
- Clean Code - Chapter 3: Functions
đ ïž 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ĂȘncia | Peso | Descrição |
|---|---|---|
| Identificação | 25% | Reconhece duplicação e seus impactos |
| Refatoração | 35% | Extrai componentes reutilizåveis corretamente |
| Qualidade | 25% | Aplica boas prĂĄticas (testes, fail-fast, etc.) |
| Aplicação | 15% | 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.