COMBATENDO CODE SMELLS EM
APLICAÇÕES JAVA
Trilha Java
Emmanuel Neri
EMMANUEL NERI
‣ Mestre em Desenvolvimento de Tecnologia
‣ Desenvolvedor desde 2010
‣ Atualmente desenvolvedor back-end na Navita
)(
PROBLEMA
O que são code smells?
COMO RESOLVER?
Como evitar códigos ruins no dia a dia?
CULTURAL
Reuso
Abstração
Código limpo
Encapsulamento
PRATICAR
POR ONDE COMEÇAR?
LEGIBILIDADE
‣ Nome claros
‣ Nome que revelem seu propósito
‣ Nomes pronunciáveis
‣ Nomes passíveis de busca
LEITURA DO CÓDIGO
BigDecimal valor = BigDecimal.ZERO;
for(Conta x : lista) {
for(ItemConta y : x.getItens()) {
if(y.getValor() != null) {
valor = valor.add(y.getValor());
}
}
BigDecimal media = valor.divide(BigDecimal.valueOf(lista.size()));
LEITURA DO CÓDIGO
BigDecimal valor = BigDecimal.ZERO;
for(Conta x : lista) {
for(ItemConta y : x.getItens()) {
if(y.getValor() != null) {
valor = valor.add(y.getValor());
}
}
BigDecimal media = valor.divide(BigDecimal.valueOf(lista.size()));
LEITURA DO CÓDIGO
BigDecimal valorTotal = BigDecimal.ZERO;
for(Conta conta : contas) {
for(ItemConta itemConta : conta.getItens()) {
if(itemConta.getValor() != null) {
valorTotal = valorTotal.add(itemConta.getValor());
}
}
}
BigDecimal quantidadeDeContas = BigDecimal.valueOf(contas.size());
BigDecimal media = valorTotal.divide(quantidadeDeContas);
S O L I D
SINGLE
RESPONSIBILITY
PRINCIPLE
OPEN / CLOSE
PRINCIPLE
LISKOVS
SUBSTITUTION
PRINCIPLE
INTERFACE
SEGREGATION
PRINCIPLE
DEPENDENCY
INVERSION
PRINCIPLE
SRP OCP LSP ISP DIP
O
"A class should have only one reason to change."
ACRÔNIMO
EXTRAINDO EM MÉTODOS
public SumarioConta sumarizarConta(List<Conta> contas) {
final BigDecimal valorTotal = calcularValorTotal(contas);
final BigDecimal quantidadeDeContas = getQuantidadeDeContas(contas);
final BigDecimal media = CalculoUtil.media(valorTotal, quantidadeDeContas);
return new SumarioConta(valorTotal, media);
}
private BigDecimal calcularValorTotal(List<Conta> contas) {
return contas.stream()
.map(Conta::getValorTotal)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
private BigDecimal getQuantidadeDeContas(List<Conta> contas) {
return BigDecimal.valueOf(contas.size());
}
TAMANHO DE CÓDIGO
‣ Classes
DUPLICAÇÃO DE CÓDIGO
public class ContaService {
public BigDecimal calcularDesconto(Conta conta) {
....
}
}
public class ItemContaService {
public BigDecimal calcularDesconto(Conta conta) {
....
}
}
public class Conta {
public BigDecimal calcularDesconto() {
....
}
}
DUPLICAÇÃO DE CÓDIGO
private static final int ESCALA_FINANCEIRA = 2;
private static final int ESCALA_CONTABIL = 6;
public BigDecimal calcularMediaFinanceira(BigDecimal total,
BigDecimal quantidade) {
return total.divide(quantidade, ESCALA_FINANCEIRA, HALF_EVEN);
}
public BigDecimal calcularMediaContabil(BigDecimal total,
BigDecimal quantidade) {
return total.divide(quantidade, ESCALA_CONTABIL, HALF_EVEN);
}
DUPLICAÇÃO DE CÓDIGO
private static final int ESCALA_FINANCEIRA = 2;
private static final int ESCALA_CONTABIL = 6;
public BigDecimal calcularMediaFinanceira(BigDecimal total,
BigDecimal quantidade) {
return total.divide(quantidade, ESCALA_FINANCEIRA, HALF_EVEN);
}
public BigDecimal calcularMediaContabil(BigDecimal total,
BigDecimal quantidade) {
return total.divide(quantidade, ESCALA_CONTABIL, HALF_EVEN);
}
DUPLICAÇÃO DE CÓDIGO
private static final int ESCALA_FINANCEIRA = 2;
private static final int ESCALA_CONTABIL = 6;
public BigDecimal calcularMediaFinanceira(BigDecimal total,
BigDecimal quantidade) {
return divide(total, quantidade, ESCALA_FINANCEIRA);
}
public BigDecimal calcularMediaContabil(BigDecimal total,
BigDecimal quantidade) {
return divide(total, quantidade, ESCALA_CONTABIL);
}
private BigDecimal divide(BigDecimal total, BigDecimal quantidade,
int escala) {
return total.divide(quantidade, escala, RoundingMode.HALF_EVEN);
}
EXCESSO DE COMPLEXIDADE
public BigDecimal calcular(Conta conta) {
if() {
if() {
}
} else if() {
} else {
if() {
} else {
}
}
}
‣ KISS ("keep it small and simple”)
public class Fatura {
private TipoFatura tipoFatura;
private Integer quantidade;
private Integer duracao;
private Integer bytes;
private BigDecimal valorUnitario;
public BigDecimal getValorTotal() {
if(tipoFatura == TipoFatura.VOZ) {
return valorUnitario.multiply(BigDecimal.valueOf(duracao));
} else if(tipoFatura == TipoFatura.DADOS) {
return valorUnitario.multiply(BigDecimal.valueOf(bytes));
} else {
return valorUnitario.multiply(BigDecimal.valueOf(quantidade));
}
}
}
public class Fatura {
private TipoFatura tipoFatura;
private Integer quantidade;
private Integer duracao;
private Integer bytes;
private BigDecimal valorUnitario;
public BigDecimal getValorTotal() {
if(tipoFatura == TipoFatura.VOZ) {
return valorUnitario.multiply(BigDecimal.valueOf(duracao));
} else if(tipoFatura == TipoFatura.DADOS) {
return valorUnitario.multiply(BigDecimal.valueOf(bytes));
} else {
return valorUnitario.multiply(BigDecimal.valueOf(quantidade));
}
}
}
public enum TipoFatura {
VOZ {
@Override
public BigDecimal calcularValorTotal(Fatura fatura) {
return multiply(fatura.getValorUnitario(), fatura.getDuracao());
}
},
DADOS {
@Override
public BigDecimal calcularValorTotal(Fatura fatura) {
return multiply(fatura.getValorUnitario(), fatura.getBytes());
}
}
public abstract BigDecimal calcularValorTotal(Fatura fatura);
}
public BigDecimal getValorTotal() {
return tipoFatura.calcularValorTotal(this);
}
PRINCIPIO DE ABERTO / FECHADO
"Software entities (classes, modules, functions, etc.) should be
open for extension, but closed for modification"
O - OPEN / CLOSE PRINCIPLE
public BigDecimal getValorTotal() {
if(tipoFatura == TipoFatura.VOZ) {
return valorUnitario.multiply(BigDecimal.valueOf(duracao));
} else if(tipoFatura == TipoFatura.DADOS) {
return valorUnitario.multiply(BigDecimal.valueOf(bytes));
} else {
return valorUnitario.multiply(BigDecimal.valueOf(quantidade));
}
}
INVEJOSO
public class ContaService {
public void resumir(Conta conta) {
final BigDecimal valorTotal = conta.getItens().stream()
.map(item -> item.getValorUnitario()
.multiply(item.getMinutos()))
.reduce(BigDecimal.ZERO, BigDecimal::add);
final BigDecimal quantidadeDeMinutos = conta.getItens().stream()
.map(ItemConta::getMinutos)
.reduce(BigDecimal.ZERO, BigDecimal::add);
final int quantidadeDeItensNaConta = conta.getItens().size();
...
}
}
INVEJOSO
public class ContaService {
public void resumir(Conta conta) {
final BigDecimal valorTotal = conta.getItens().stream()
.map(item -> item.getValorUnitario()
.multiply(item.getMinutos()))
.reduce(BigDecimal.ZERO, BigDecimal::add);
final BigDecimal quantidadeDeMinutos = conta.getItens().stream()
.map(ItemConta::getMinutos)
.reduce(BigDecimal.ZERO, BigDecimal::add);
final int quantidadeDeItensNaConta = conta.getItens().size();
...
}
}
FEATURE ENVY
public class ContaService {
public void resumir(Conta conta) {
final BigDecimal valorTotal = conta.calcularValorTotal();
final BigDecimal quantidadeDeMinutos =
conta.calcularQuantidadeDeMinutos();
final BigDecimal quantidadeDeItensNaConta =
conta.getQuantidadeDeItensNaConta();
...
}
}
NÍVEL DE MÉTODO
‣ Muitos parâmetros
public List<Conta> buscar(LocalDate dataInicio, LocalDate dataFim,
String cliente, String identificador,
String descricao) {
}
public List<Conta> buscar(ContaFiltroTO contaFiltro) {
}
NÍVEL DE MÉTODO
‣ Parâmetros de saída
public void pagarConta(Conta conta) {
conta.setStatus(StatusConta.PAGA);
}
conta.pagar();
NÍVEL DE MÉTODO
‣ Parâmetros lógicos
public void pagar(boolean isAtrasada, boolean isNotificarPagamento) {
if(isAtrasada) {
conta.pagaAtrasada();
} else {
conta.pagar();
}
if(isNotificarPagamento) {
notificarPagamento();
}
}
public void pagarAtrasada() { }
public void pagarNoPrazo() { }
public void notificarPagamento() { }
OUTROS
‣ Excessivo retorno de dados
‣ Lazy class
‣ Intimidade inapropriada
‣ Uso de Switch
‣ Refused bequest (Liskov Substitution Principle)
‣ Atributos de classe temporários
‣ Data clumps
‣ Dead code
‣ ….
FERRAMENTAS
‣ SonarQube
‣ FindBugs
‣ IDEs
DASHBOARD SONAR
https://github.com/emmanuelneri/productivity-with-spring/blob/master/pom.xml#L80
USANDO SONAR
RECOMENDAÇÃO
OBRIGADO
emmanuelnerisouza@gmail.com
https://github.com/emmanuelneri
@emmanuelnerii
www.emmanuelneri.com.br
“VOCÊ SEMPRE DEVE DEIXAR O LUGAR
MAIS LIMPO DO QUE ENCONTROU”

Combatendo code smells em aplicações Java

  • 1.
    COMBATENDO CODE SMELLSEM APLICAÇÕES JAVA Trilha Java Emmanuel Neri
  • 2.
    EMMANUEL NERI ‣ Mestreem Desenvolvimento de Tecnologia ‣ Desenvolvedor desde 2010 ‣ Atualmente desenvolvedor back-end na Navita )(
  • 3.
    PROBLEMA O que sãocode smells?
  • 4.
    COMO RESOLVER? Como evitarcódigos ruins no dia a dia?
  • 5.
  • 6.
  • 7.
  • 8.
    LEGIBILIDADE ‣ Nome claros ‣Nome que revelem seu propósito ‣ Nomes pronunciáveis ‣ Nomes passíveis de busca
  • 9.
    LEITURA DO CÓDIGO BigDecimalvalor = BigDecimal.ZERO; for(Conta x : lista) { for(ItemConta y : x.getItens()) { if(y.getValor() != null) { valor = valor.add(y.getValor()); } } BigDecimal media = valor.divide(BigDecimal.valueOf(lista.size()));
  • 10.
    LEITURA DO CÓDIGO BigDecimalvalor = BigDecimal.ZERO; for(Conta x : lista) { for(ItemConta y : x.getItens()) { if(y.getValor() != null) { valor = valor.add(y.getValor()); } } BigDecimal media = valor.divide(BigDecimal.valueOf(lista.size()));
  • 11.
    LEITURA DO CÓDIGO BigDecimalvalorTotal = BigDecimal.ZERO; for(Conta conta : contas) { for(ItemConta itemConta : conta.getItens()) { if(itemConta.getValor() != null) { valorTotal = valorTotal.add(itemConta.getValor()); } } } BigDecimal quantidadeDeContas = BigDecimal.valueOf(contas.size()); BigDecimal media = valorTotal.divide(quantidadeDeContas);
  • 12.
    S O LI D SINGLE RESPONSIBILITY PRINCIPLE OPEN / CLOSE PRINCIPLE LISKOVS SUBSTITUTION PRINCIPLE INTERFACE SEGREGATION PRINCIPLE DEPENDENCY INVERSION PRINCIPLE SRP OCP LSP ISP DIP O "A class should have only one reason to change." ACRÔNIMO
  • 13.
    EXTRAINDO EM MÉTODOS publicSumarioConta sumarizarConta(List<Conta> contas) { final BigDecimal valorTotal = calcularValorTotal(contas); final BigDecimal quantidadeDeContas = getQuantidadeDeContas(contas); final BigDecimal media = CalculoUtil.media(valorTotal, quantidadeDeContas); return new SumarioConta(valorTotal, media); } private BigDecimal calcularValorTotal(List<Conta> contas) { return contas.stream() .map(Conta::getValorTotal) .reduce(BigDecimal.ZERO, BigDecimal::add); } private BigDecimal getQuantidadeDeContas(List<Conta> contas) { return BigDecimal.valueOf(contas.size()); }
  • 14.
  • 15.
    DUPLICAÇÃO DE CÓDIGO publicclass ContaService { public BigDecimal calcularDesconto(Conta conta) { .... } } public class ItemContaService { public BigDecimal calcularDesconto(Conta conta) { .... } } public class Conta { public BigDecimal calcularDesconto() { .... } }
  • 16.
    DUPLICAÇÃO DE CÓDIGO privatestatic final int ESCALA_FINANCEIRA = 2; private static final int ESCALA_CONTABIL = 6; public BigDecimal calcularMediaFinanceira(BigDecimal total, BigDecimal quantidade) { return total.divide(quantidade, ESCALA_FINANCEIRA, HALF_EVEN); } public BigDecimal calcularMediaContabil(BigDecimal total, BigDecimal quantidade) { return total.divide(quantidade, ESCALA_CONTABIL, HALF_EVEN); }
  • 17.
    DUPLICAÇÃO DE CÓDIGO privatestatic final int ESCALA_FINANCEIRA = 2; private static final int ESCALA_CONTABIL = 6; public BigDecimal calcularMediaFinanceira(BigDecimal total, BigDecimal quantidade) { return total.divide(quantidade, ESCALA_FINANCEIRA, HALF_EVEN); } public BigDecimal calcularMediaContabil(BigDecimal total, BigDecimal quantidade) { return total.divide(quantidade, ESCALA_CONTABIL, HALF_EVEN); }
  • 18.
    DUPLICAÇÃO DE CÓDIGO privatestatic final int ESCALA_FINANCEIRA = 2; private static final int ESCALA_CONTABIL = 6; public BigDecimal calcularMediaFinanceira(BigDecimal total, BigDecimal quantidade) { return divide(total, quantidade, ESCALA_FINANCEIRA); } public BigDecimal calcularMediaContabil(BigDecimal total, BigDecimal quantidade) { return divide(total, quantidade, ESCALA_CONTABIL); } private BigDecimal divide(BigDecimal total, BigDecimal quantidade, int escala) { return total.divide(quantidade, escala, RoundingMode.HALF_EVEN); }
  • 19.
    EXCESSO DE COMPLEXIDADE publicBigDecimal calcular(Conta conta) { if() { if() { } } else if() { } else { if() { } else { } } } ‣ KISS ("keep it small and simple”)
  • 20.
    public class Fatura{ private TipoFatura tipoFatura; private Integer quantidade; private Integer duracao; private Integer bytes; private BigDecimal valorUnitario; public BigDecimal getValorTotal() { if(tipoFatura == TipoFatura.VOZ) { return valorUnitario.multiply(BigDecimal.valueOf(duracao)); } else if(tipoFatura == TipoFatura.DADOS) { return valorUnitario.multiply(BigDecimal.valueOf(bytes)); } else { return valorUnitario.multiply(BigDecimal.valueOf(quantidade)); } } }
  • 21.
    public class Fatura{ private TipoFatura tipoFatura; private Integer quantidade; private Integer duracao; private Integer bytes; private BigDecimal valorUnitario; public BigDecimal getValorTotal() { if(tipoFatura == TipoFatura.VOZ) { return valorUnitario.multiply(BigDecimal.valueOf(duracao)); } else if(tipoFatura == TipoFatura.DADOS) { return valorUnitario.multiply(BigDecimal.valueOf(bytes)); } else { return valorUnitario.multiply(BigDecimal.valueOf(quantidade)); } } }
  • 22.
    public enum TipoFatura{ VOZ { @Override public BigDecimal calcularValorTotal(Fatura fatura) { return multiply(fatura.getValorUnitario(), fatura.getDuracao()); } }, DADOS { @Override public BigDecimal calcularValorTotal(Fatura fatura) { return multiply(fatura.getValorUnitario(), fatura.getBytes()); } } public abstract BigDecimal calcularValorTotal(Fatura fatura); } public BigDecimal getValorTotal() { return tipoFatura.calcularValorTotal(this); }
  • 23.
    PRINCIPIO DE ABERTO/ FECHADO "Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification" O - OPEN / CLOSE PRINCIPLE public BigDecimal getValorTotal() { if(tipoFatura == TipoFatura.VOZ) { return valorUnitario.multiply(BigDecimal.valueOf(duracao)); } else if(tipoFatura == TipoFatura.DADOS) { return valorUnitario.multiply(BigDecimal.valueOf(bytes)); } else { return valorUnitario.multiply(BigDecimal.valueOf(quantidade)); } }
  • 24.
    INVEJOSO public class ContaService{ public void resumir(Conta conta) { final BigDecimal valorTotal = conta.getItens().stream() .map(item -> item.getValorUnitario() .multiply(item.getMinutos())) .reduce(BigDecimal.ZERO, BigDecimal::add); final BigDecimal quantidadeDeMinutos = conta.getItens().stream() .map(ItemConta::getMinutos) .reduce(BigDecimal.ZERO, BigDecimal::add); final int quantidadeDeItensNaConta = conta.getItens().size(); ... } }
  • 25.
    INVEJOSO public class ContaService{ public void resumir(Conta conta) { final BigDecimal valorTotal = conta.getItens().stream() .map(item -> item.getValorUnitario() .multiply(item.getMinutos())) .reduce(BigDecimal.ZERO, BigDecimal::add); final BigDecimal quantidadeDeMinutos = conta.getItens().stream() .map(ItemConta::getMinutos) .reduce(BigDecimal.ZERO, BigDecimal::add); final int quantidadeDeItensNaConta = conta.getItens().size(); ... } }
  • 26.
    FEATURE ENVY public classContaService { public void resumir(Conta conta) { final BigDecimal valorTotal = conta.calcularValorTotal(); final BigDecimal quantidadeDeMinutos = conta.calcularQuantidadeDeMinutos(); final BigDecimal quantidadeDeItensNaConta = conta.getQuantidadeDeItensNaConta(); ... } }
  • 27.
    NÍVEL DE MÉTODO ‣Muitos parâmetros public List<Conta> buscar(LocalDate dataInicio, LocalDate dataFim, String cliente, String identificador, String descricao) { } public List<Conta> buscar(ContaFiltroTO contaFiltro) { }
  • 28.
    NÍVEL DE MÉTODO ‣Parâmetros de saída public void pagarConta(Conta conta) { conta.setStatus(StatusConta.PAGA); } conta.pagar();
  • 29.
    NÍVEL DE MÉTODO ‣Parâmetros lógicos public void pagar(boolean isAtrasada, boolean isNotificarPagamento) { if(isAtrasada) { conta.pagaAtrasada(); } else { conta.pagar(); } if(isNotificarPagamento) { notificarPagamento(); } } public void pagarAtrasada() { } public void pagarNoPrazo() { } public void notificarPagamento() { }
  • 30.
    OUTROS ‣ Excessivo retornode dados ‣ Lazy class ‣ Intimidade inapropriada ‣ Uso de Switch ‣ Refused bequest (Liskov Substitution Principle) ‣ Atributos de classe temporários ‣ Data clumps ‣ Dead code ‣ ….
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.

Notas do Editor

  • #2 Visando um código limpo Para fácil manutenção
  • #4 propícios a erros Causa confusão Difícil compreensão Ruins para manutenibilidade
  • #6 Pensar a todo momento Conhecer as boas práticas Independente do tamanho de código Até que código ruins nos incomode
  • #7 E colocar em prática! "Não da tempo”, não cabe nada tarefa
  • #8 Apagar tudo e fazer de novo? Precisamos de testes
  • #9 Não impacta no resultado, apenas na apresentação Code = Leitura de um livro (Clean code)
  • #10 Concordam não é uma leitura fluente?
  • #11 Concordam não é uma leitura fluente? Exige interpretação
  • #12 Concordam que tem uma leitura melhor? Tem fluência
  • #13 S: Uma classe só deve ter uma responsabilidade O: Nossas entidades devem ser abertas para evoluções mais fechadas para alterações L: Classes derivadas devem poder ser substituídas por suas classes base I: Entidades não devem ser forçados a depender de métodos que não usam D: Abstrações não devem depender de detalhes. Detalhes devem depender de abstrações.
  • #14 Extrair Método pequenos Melhor para testar
  • #15 Probabilidade de estar fazendo mais de uma coisa (SRP) Quebrar a classe em classes menores  (God Object)
  • #16 The Rule of Three
  • #17 Chamar atenção Códigos parecidos Nem sempre são identificados em ferramentas
  • #18 Chamar atenção Nem sempre são identificados em ferramentas
  • #19 Extrair para reaproveitar Sobrecarga (OO)
  • #20 Existem código complexos + nós complicados códigos simples Complexidade cíclomatica Quantidade de fluxo …
  • #21 qual o problema desse código? Muda comportamento
  • #22 qual o problema desse código?
  • #23 Usufruir do Polimorfismo - complexidade + constência
  • #25 Cuidado com o Invejoso Get demais de outro objeto
  • #26 Viola Encapsulamento Get demais de outro objeto
  • #27 Método que acesso muito dados de outro
  • #28 Além do tamanho, … Até 3 parâmetros
  • #29 Imutabilidade
  • #30 Sentimento de estar fazendo mais de uma coisa + complexidade ciclomática
  • #31 - Liskov: Classes derivadas devem poder ser substituídas por suas classes base
  • #34 - Iteração ineficiente de Map