Em busca de qualidade em aplicações Java, a apresentação tem como objetivo combater code smells com técnicas de clean code, SOLID, entre outros.
Apresentação TDC São Paulo 2017, trilha de Java
12. 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
13. 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());
}
15. 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() {
....
}
}
16. 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);
}
17. 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);
}
18. 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);
}
19. EXCESSO DE COMPLEXIDADE
public BigDecimal calcular(Conta conta) {
if() {
if() {
}
} else if() {
} else {
if() {
} else {
}
}
}
‣ KISS ("keep it small and simple”)
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 class ContaService {
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 retorno de dados
‣ Lazy class
‣ Intimidade inapropriada
‣ Uso de Switch
‣ Refused bequest (Liskov Substitution Principle)
‣ Atributos de classe temporários
‣ Data clumps
‣ Dead code
‣ ….
propícios a erros
Causa confusão
Difícil compreensão
Ruins para manutenibilidade
Pensar a todo momento
Conhecer as boas práticas
Independente do tamanho de código
Até que código ruins nos incomode
E colocar em prática!
"Não da tempo”, não cabe nada tarefa
Apagar tudo e fazer de novo?
Precisamos de testes
Não impacta no resultado, apenas na apresentação
Code = Leitura de um livro (Clean code)
Concordam não é uma leitura fluente?
Concordam não é uma leitura fluente?
Exige interpretação
Concordam que tem uma leitura melhor?
Tem fluência
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.
Extrair
Método pequenos
Melhor para testar
Probabilidade de estar fazendo mais de uma coisa (SRP)
Quebrar a classe em classes menores
(God Object)
The Rule of Three
Chamar atenção
Códigos parecidos
Nem sempre são identificados em ferramentas
Chamar atenção
Nem sempre são identificados em ferramentas
Extrair para reaproveitar
Sobrecarga (OO)
Existem código complexos
+ nós complicados códigos simples
Complexidade cíclomatica
Quantidade de fluxo …
qual o problema desse código?
Muda comportamento
qual o problema desse código?
Usufruir do Polimorfismo
- complexidade
+ constência
Cuidado com o Invejoso
Get demais de outro objeto
Viola Encapsulamento
Get demais de outro objeto
Método que acesso muito dados de outro
Além do tamanho, …
Até 3 parâmetros
Imutabilidade
Sentimento de estar fazendo mais de uma coisa
+ complexidade ciclomática
- Liskov: Classes derivadas devem poder ser substituídas por suas classes base