Este documento apresenta conceitos de código limpo em exemplos de código. Apresenta quatro partes: uma sobre filosofia, outra sobre teoria, outra sobre prática em exemplos de código e uma sobre laboratório de refatoração. Apresenta conceitos como DDD, SRP, DRY, testes, refatoração e regras de simplicidade aplicados em exemplos de código.
1. Clean Code na Prática
Conceitos de Clean Code Apresentados
em Exemplos de Código
Douglas Siviotti
Novembro de 2019 V1.1
2. Sobre esta Apresentação
1. Conteúdo: Clean Code (Código Limpo)
2. Área/Foco: Engenharia de Software, Desenvolvimento Ágil
3. Público alvo: Desenvolvedores
4. Conteúdo relacionado: OO, Programação Funcional, DDD, SOLID, GRASP
Organização (155 slides)
Parte 1 – Um Pouco de Filosofia
Parte 2 – Um Pouco de Teoria (Conceitos Relacionados)
Parte 3 – Clean Code na Prática em Exemplos de Código
Parte 4 – Laboratório de Refatoração
Código-fonte usado nesta apresentação:
https://github.com/refactown/cleancode
Cognição
Micro Design
Integração
Macro Design
3. Sintomas
comuns
equipe entregando cada vez menos
pontos a cada sprint
estimativas frustradas: o esforço
é sempre maior que o previsto
erros
frequentes em
produção
correção de um bug causa
outros problemas não previstos
incidentes resolvidos e o mesmo
erro voltando a aparecer
muito “To Do” espalhado pelo
código e/ou código morto
dificuldade de evolução ou
incorporação de novas
funcionalidades
4. Código Ruim
manutenção difícil
+ retrabalho e - produtividade
Código Ruim
manutenção difícil
+ retrabalho e - produtividade
Procrastinação
aversão a mudanças
criação de dívida técnica
Procrastinação
aversão a mudanças
criação de dívida técnica
Falta de Testes
design pobre e inflexível
falta de segurança p/ o dev
Falta de Testes
design pobre e inflexível
falta de segurança p/ o dev
Pressão
bugs, incidentes, pouco valor,
baixa evolução: insatisfação
Pressão
bugs, incidentes, pouco valor,
baixa evolução: insatisfação
ciclo
vicioso da
dívida
técnica
5. Código Limpo
fácil de entender e manter
expressa o negócio
Código Limpo
fácil de entender e manter
expressa o negócio
Refatoração
adaptação ao novo problema
tratamento de dívida técnica
Refatoração
adaptação ao novo problema
tratamento de dívida técnica
Teste de Unidade
guiando o design (feedback)
com boa cobertura
Teste de Unidade
guiando o design (feedback)
com boa cobertura
Entregas c/ Valor
evolução sustentável
cliente satisfeito
Entregas c/ Valor
evolução sustentável
cliente satisfeito
ciclo da
eficácia no
software
sob medida
6. Clean Code
Fazer código para ser lido e
entendido por outras pessoas
Clean Code
Fazer código para ser lido e
entendido por outras pessoas
Refatoração
adaptação ao novo problema
tratamento de dívida técnica
Teste de Unidade
guiando o design (feedback)
com boa cobertura
Entregas c/ Valor
evolução sustentável
cliente satisfeito
ciclo da
eficácia no
software
sob medida
9. código é detalhamentocódigo é detalhamento
dos requisitos e devedos requisitos e deve
expressar o negócioexpressar o negócio
10. código deve ser feito para ser lido por outra pessoacódigo deve ser feito para ser lido por outra pessoa
comunique,
não codifique!
11. código ruim geracódigo ruim gera
baixa produtividadebaixa produtividade
que gera código piorque gera código pior
seja um
glóbulo branco
prevenir é melhor
que remediar
por que
12. CódigoCódigo
Limpo?Limpo?
“elegante e eficiente
faz apenas uma coisa”
Bjarne Stroustrup
“simples e direto - legível”
Grady Booch
“inteligível ao seres humanos”
Dave Thomas
“escrito por alguém
que se importava”
Michael Feathers
“sem duplicação, expressivo,
pequenas abstrações”
Ron Jeffries
“a linguagem parece que foi
feita para o problema”
Ward Cunningham
O que é
13. A T I T U D EA T I T U D E
teoria da janela
quebrada
regra do
escoteiro
quando
programador
profissional
quem
quando
14. técnica, treino e tempotécnica, treino e tempo
livros de arte não
fazem um artista
trabalho artesanal
mas profissional
como
15. Um Pouco de Teoria*Um Pouco de Teoria*
*Oriunda de muita prática*Oriunda de muita prática
Parte 2
16. Título do Slide (Arial 18)
16/155
2.1 DDD: Negócio ao Código
2.2 Linguagem Onipresente
2.3 Princípio SRP
2.4 Princípio DRY
2.5 Indireção / Delegação
2.6 Lei de Demeter
2.7 Acoplamento e Coesão
2.8 Imutabilidade
2.9 OO + Funções Puras
2.10 Testes Tempestivos
2.11 Refatoração Tempestiva
2.12 Regras da Simplicidade
Conceitos
relacionados
17. O requisito está no código, mas muito código não vem do requisito
O domínio de um problema organiza os conceitos sobre os quais é baseada a solução de software
Foco no Negócio
Parte do código deve expressar o negócio
Isolamento do Domínio
O código de domínio é idiossincrático e particular
Infraestrutura à Parte
Tecnologias vêm e vão, o negócio permanece
Do Negócio ao Código Via DDD 2.1 Negócio ao Código
Domain
18. DDD e os Nomes das Coisas 2.2 Linguagem Onipresente
Ubiquos
Language
Service
Entity
Value
Object
Agregate
Factory
Model-driven
Design
Bounded
Context
RepositoryDomain
Events
Layered
Architecture
Core
Domain
Context
Map
Continuos
Integration
Linguagem Onipresente (Ubíqua)
- Termos e conceitos usados pelos desenvolvedores e pelos
especialistas no negócio
- Presente na fala de todos e no código de domínio
Contexto Delimitado
- Partes bem delimitadas de um domínio
- Candidatos a microsserviços
Tijolos de Construção
- Entidades, Objetos de Valor, Agregados
- Eventos de Domínio
- Serviços, Fábricas, Repositórios
19. Princípio da Responsabilidade Única (SRP) 2.3 Princípio SRP
“Cada módulo de um software deve ter um e apenas um motivo para mudar”
1
O que seria
apenas “uma”
responsabilidade?
20. Menos é Mais 2.4 Princípio DRY
DRY = Don’t Repeat Yourself
“Não Se Repita” (código, requisito, teste etc)
Toda parte do conhecimento deve ter
uma representação única e livre
de ambiguidades em todo o sistema
Problemas
- Mais trabalho: alterar mais vezes
- Mais risco: encontrar todos os lugares
- Mais teste: testar várias vezes a mesma coisa
- Mais esforço de comunicação
21. Indireção ou Delegação 2.5 Indireção / Delegação
SRP
Fazer apenas
uma coisa em
um certo lugar
SRP
Fazer apenas
uma coisa em
um certo lugar
DRY
Fazer apenas
em um lugar
uma certa coisa
DRY
Fazer apenas
em um lugar
uma certa coisa
Coisa única
em um
único lugar
“A”
Coisa única
em um
único lugar
“A”
Outra
coisa única
em um
único lugar
“B”
Outra
coisa única
em um
único lugar
“B”
Indireção ou Delegação
1. Não pode conter
2. Não pode copiar
3. Precisa referenciar
Indireção Indireção
Calculo de Frete Busca de EndereçoEfetuar Pedido
22. Lei de Demeter – Princípio do Menor Conhecimento 2.6 Lei de Demeter
Um módulo não deve enxergar o interior
dos objetos que ele manipula
Um método “m” de uma classe “C” só deve chamar:
- Objetos do tipo “C”
- Parâmetros passados para o método “m” (e.g. “x: P”)
- Um objeto criado pelo próprio método “m”
- Variáveis de instância da classe “C” (e.g. “a: A”)
Train Wreck (Acidente de Trem): x.getY().getZ()
“Você não manda as pernas do cachorro andarem e sim o cachorro”
Classe “C”
Atributo a: A
Método “m” (x: X)
Classe X
Atributo y : Y
Classe Y
Atributo z : Z
23. Baixo Acoplamento e Alta Coesão 2.7 Acoplamento e Coesão
Alto Acoplamento
Baixa Coesão
(entre classes)
Poucas classes e grandes
Mais dependência por classe
Maior conhecimento
Baixo Acoplamento
Alta Coesão
(entre classes)
Muitas classes e pequenas
Menos dependência por classe
Menor conhecimento
Coesão de uma Classe:
- Poucas variáveis de instância
- Cada método manipula uma ou mais
Uma classe pouco coesa sugere
que uma nova classe deve “nascer”
24. Quanto Mais Imutável Melhor 2.8 Imutabilidade
Evite Surpresas
Imutabilidade é uma defesa contra
efeitos colaterais
Vantagens
- Mais seguro e confiável
- Mais legível:
minimiza testes e garantias
diminui o código por tabela
- Paralelismo e escalabilidade
private final int idade;
25. Quanto Mais Puro Melhor 2.9 OO + Funções Puras
Função Pura
1. Para um certo Input sempre retorna o mesmo Output
2. Não causa nenhum efeito colateral
3. Não depende de nenhum dado externo (autocontida)
Vantagens
- Mais simples e menores
- Mais fácil de entender e testar
Quando usar em código OO?
Sempre que possível
Em muitas situações não é possível: BD, integração, estados etc
Possível em regras de negócio, métodos utilitários, internos etc
Function
Input
Output
IMUTABILIDADE
- efeitos colaterais
26. TDD e Testes Tempestivos 2.10 Testes Tempestivos
TDD – Test Driven Development
(Desenvolvimento Guiado por Testes)
- Escreve um teste que falha antes do código produtivo
- Faz o teste passar ao construir o código produtivo
- Refatora o código até o teste falhar (ou não)
Testes Tempestivos (TDD inclusive)
(teste que ajuda na construção, logo cedo)
1. Atividade de engenharia (código e design)
2. Feedback rápido sobre o código testado (erra cedo)
3. Gera código limpo através da “dificuldade” de testar
4. Antecipa o custo gasto em correções e debug
5. Ainda serve como teste de regressão
Refatora
Falha
Passa
Ciclos
Curtos
27. Refatoração Tempestiva 2.11 Refatoração
“Refatoração (refactoring) é a disciplina técnica
para reestruturar um determinado código,
alterando sua estrutura interna sem mudar seu
comportamento externo”
Martin Fowler
Problema Inicial Solução Inicial
Problema Refinado
Refatoração
Mudança
Solução Final
Validar Solução
Refinamento
Ciclo de
Sprint
Ajuste da solução em função de:
- uma nova visão sobre o problema
- preparar para uma evolução ou mudança
Pequenas mudanças que fazem
diferença no longo prazo
28. Quatro Regras da Simplicidade (Martin Fowler : Kent Beck) 2.12 Simplicidade
Testes Tempestivos
Refatoração Tempestiva
https://martinfowler.com/bliki/BeckDesignRules.html
29. Conceitos Relacionados Conceitos
DDDDDD Linguagem
Onipresente
Linguagem
Onipresente
Indireção
Abstração, especialização
Indireção
Abstração, especialização
Lei de Demeter
Mínimo conhecimento
Lei de Demeter
Mínimo conhecimento
Princípio SRP
Responsabilidade Única
Princípio SRP
Responsabilidade Única
Princípio DRY
Não Se Repita
Princípio DRY
Não Se Repita
Acoplamento
e Coesão
Acoplamento
e Coesão
Funções Puras
OO ou funcional
Funções Puras
OO ou funcionalInfraInfra
DomínioDomínio
AppApp
Refatoração
Tempestiva
Refatoração
Tempestiva
Teste
Tempestivo
Teste
Tempestivo
4 Regras da
Simplicidade
4 Regras da
Simplicidade
Imutabilidade
- efeitos colaterais
Imutabilidade
- efeitos colaterais
Clean
Code
Clean
Code
31. Título do Slide (Arial 18)
31/155
3.1 Código Limpo
3.2 Nomes Significativos
3.3 Comentários (capítulo 4)
3.4 Formatação (capítulo 5)
3.5 Funções (capítulo 3)
3.6 Objetos e E. de Dados
3.7 Tratamento de Erro
3.8 Fronteiras (Limites)
3.9 Testes de Unidade
3.10 Classes
3.11 Sistemas Complexos
3.12 Simplicidade
Prática
- filosofia
- bad smell
- boa prática
- exemplo
- exercício
Cognição
Entendimento e leitura
Integração
Contratos e expectativas
Macro Design
Escala e expansão
Micro Design
Estado e comportamento
33. Filosofia sobre Código Limpo (Visão Geral) 3.1 Código Limpo
- O código não fica nem se mantém limpo sozinho
- Todos na equipe devem praticar e cobrar
- Manter o código limpo é uma atividade diária
- Funciona como na “Teoria das janelas quebradas”
- Atitude é mais relevante do que “patrocínio”
- Sempre dá pra começar (regra do escoteiro)
- Ler e entender consome mais tempo que escrever
- Precisa de tempo, técnica e treino pra fazer bem
34. Código Ruim 3.1 Código Limpo
Problemas
- Nomes ruins
- Função grande e complexa (SRP)
- Comentários questionáveis
- Formatação inexistente
Dificuldades
- Legibilidade: confuso
- Testabilidade: alta complexidade
35. Código Menos Ruim 3.1 Código Limpo
Problemas
- Nomes ruins
- Função grande e complexa (SRP)
- Comentários questionáveis
- Formatação inexistente
Dificuldades
- Legibilidade: confuso
- Testabilidade: alta complexidade
39. Filosofia sobre Nomes 3.2 Nomes Significativos
- Nomes são muito importantes (especialmente em OO)
- Use nomes que revelam seu propósito
- Evite informações erradas (itemList: Map)
- Faça distinções significativas (evite a1, a2 a3...)
- Nomes pronunciáveis e passíveis de busca
- Evite codificações (e.g. notação húngara e similares)
- classes = substantivos, métodos = verbos
- Selecione uma palavra por conceito (get, set, find)
- Nomes do problema (Pedido, Item) e da solução (DAO)
- Adicione ou evite contexto quando necessário
40. Bad Smell 3.2 Nomes Significativos
Problemas
- Nomes genéricos “i”, “x”, “d” etc
- Nomes impronunciáveis
- Número Mágico (perdido no código)
- Notação Húngara
- Interface prefixada
- Nome com contexto redundante
41. Boa Prática 3.2 Nomes Significativos
Alternativas
- Nomes que revelam propósito
- Nomes pronunciáveis
- Constantes para número mágico
(passíveis de busca)
- Sem Notação Húngara
- Interface = Conceito
- Nomes com o mínimo necessário
43. Modelo de Exemplo 3.2 Nomes Significativos
Nomes a partir do domínio do problema (Pedido, Item etc)
Nomes a partir do domínio da solução (Controller, Delegate, DAO)
Classes = Substantivos, Métodos = Verbos
Nomes pronunciáveis e passíveis de busca
45. Filosofia sobre Comentários 3.3 Comentários
“Qualquer um pode escrever código que o computador
entenda. Bons programadores escrevem códigos que os
humanos entendem.”
Martin Fowler
- Comentários são a última opção do programador
- O programador deve expressar-se no código
- Comunique, não codifique (outros devem ler)
- Comentário pode estar compensando código ruim
- Alguns comentários são bons ou necessários
46. Explique-se no Código 3.3 Comentários
Alguns comentários existem para explicar código ruim.
Um comentário deve explicar “o porquê” e nunca “o que”
Não comente, refatore!
51. Outros Comentários Ruins 3.3 Comentários
- Ruidosos, muito longos ou repetitivos
- Imperativos (e.g. em todas as funções)
- Marcadores de Posição //////////////// ou ************
- Ao lado de chaves de fechamento (e.g. //for)
- Comentários em HTML
- Informações excessivas
- Conexões nada óbvias
- Cabeçalhos de funções (prefira um bom nome)
- Javadoc em código não público
53. Filosofia sobre Formatação 3.4 Formatação
- Atenção ao “code convention” da linguagem
- O projeto deve ter um “code style” (bom ou ruim)
- Qualquer “code style” é melhor que nenhum
- A formatação automática evita muitas brigas
- Formatação vertical
Metáfora do jornal, distância vertical, espaçamento e
continuidade vertical
- Formatação horizontal
Indentação, espaçamento e continuidade horizontal
55. Formatação Vertical: Continuidade e Espaçamento Vertical 3.4 Formatação
Linhas de código relacionadas
devem permanecer unidas
Pensamentos completos devem ser
separados por espaços em branco
56. Formatação Vertical: Distância Vertical 3.4 Formatação
Declaração de Variável
O mais próximo possível de
onde será usada
Variáveis de Instância
Antes dos métodos
Funções Dependentes
Verticalmente próximas
Afinidade Conceitual
Mesma convenção de nomes
atuam na mesma tarefa
devem ficar próximas
57. Formatação Horizontal 3.4 Formatação
Evite
- Espaçamentos manuais
- Indentação do “seu gosto”
- Mais de 120 caracteres por linha
Deixe a IDE trabalhar
- Espaçamentos automáticos
- Indentação padrão
- Quebras de linha
58. Regra da Tela 3.4 Formatação
<= 20
Cabe na tela
<= 500
100-120
Cabe na tela
59. Code Convention + Regra da Equipe 3.4 Formatação
Mantenha a paz no seu projeto: tenha um “code style” combinado pela equipe
61. Filosofia sobre Funções 3.5 Funções
- Funções devem ser pequenas (<= 20 linhas)
- Funções devem fazer apenas uma coisa
- Devem ter nomes descritivos do que fazem
- Somente um nível de abstração por função
- Funções devem ter poucos parâmetros
- Efeitos colaterais devem ser evitados ao máximo
- Quanto mais funções puras, melhor
- Quanto mais autocontida mais fácil de testar
63. Regra 1 3.5 Funções
Faz Apenas 1 Coisa
- Nomes, comentários e formatação são importantes para a
leitura, mas as funções são o que o programa realmente faz
- É melhor ler e entender uma coisa de cada vez
- É melhor testar uma coisa de cada vez
1 Nível de Abstração
- Cada conceito em seu lugar apropriado
Abstração é a capacidade de separar o essencial do que
não é essencial. Separar conceitos dos detalhes.
64. 3.5 FunçõesExemplo de Função “Suja”
Grande (34 linhas)
Faz muitas coisas:
1. Valida os números apostados
2. Verifica se está entre 6 e 15
3. Sorteia 6 números
4. Conta os acertos
5. Calcula o prêmio
Vários níveis de
abstração
1
2
3
4
5
65. Exemplo de Função “Limpa” 3.5 Funções
Pequena (11 linhas)
Faz 1 coisa:
1. Valida os números apostados
2. Verifica se está entre 6 e 15
3. Sorteia 6 números
4. Conta os acertos
5. Calcula o prêmio
1 nível de abstração
1 coisa e 1 nível de abstração
Indireção
66. Regra Decrescente 3.5 Funções
Indireção Nível de abstração 1
Nível de abstração 2
Leitura
de cima
para
baixo
Nomes
descritivos
do que
fazem
67. Switch Highlander (Java) 3.5 Funções
Um “Switch” deve estar em
uma classe de nível mais
baixo de abstração e
nunca ser repetido
Opção procedural
Opção polimórfica
68. Switch Highlander (Kotlin) 3.5 Funções
Um “Switch” deve estar em
uma classe de nível mais
baixo de abstração e
nunca ser repetido
Opção procedural
Opção polimórfica
69. Fibonacci (Funções Puras) 3.5 Funções
1. Para um certo Input sempre retorna o mesmo Output
2. Não causa nenhum efeito colateral
3. Não depende de nenhum dado externo (escopo)
70. Pedra, Papel e Tesoura: Especificação via Teste de Unidade 3.5 Funções
R (Rock/Pedra) vence S (Scissors/Tesoura)
S (Scissors/Tesoura) vence P (Paper/Papel)
P (Paper/Papel) vence R (Rock/Pedra)
71. Pedra, Papel e Tesoura: Contrato 3.5 Funções
Cabeçalho da Função:
pedraPapelTesoura(char,char):int
Contrato da Função:
- Recebe dois caracteres (R, P ou S)
- Se empata retorna 0
- Se o primeiro ganha retorna -1
- Se o segundo ganha retorna 1
- Se um dos caracteres é inválido gera uma
exceção “IllegalArgumentException”
(melhor que código de erro)
- R vence S, S vence P, P vence R
72. Pedra, Papel e Tesoura (Kotlin) 3.5 Funções
1 Nível de abstração
Faz apenas 1 coisa
Prefira exceções a retorno de códigos de erro!
É uma função pura?
73. Pedra, Papel e Tesoura (Java) 3.5 Funções
É uma função pura?
74. Parâmetros 3.5 Funções
Função Mônade P1
Função Díade
Função Tríade P1 P2 P3
P1 P2
A quantidade de parâmetros deve ser a
menor possível. Se a quantidade de
parâmetros é muito grande, pode ser usado
um objeto como parâmetro.
Objeto como Parâmetro
O parâmetro tem suas regras e seus próprios conceitos
75. Parâmetros Seletores e de “Saída” 3.5 Funções
Parâmetro de “Saída”
Saída deve ser no retorno
Parâmetro Seletor
É preferível dividir em duas funções
Um parâmetro seletor já indica logo
na assinatura que uma função faz
duas coisas. O ideal é fazer
duas funções separadas.
76. Separação Comando-Consulta 3.5 Funções
Comando + Consulta
Apenas Consulta
Apenas Comando
As funções devem somente
fazer ou responder
algo, mas não ambos. Ou altera
o estado de um objeto ou
retorna informação sobre ele.
77. Tratamento de Erro é 1 Coisa Só 3.5 Funções
O “try” deve ser a primeira linha da função e não deve
haver nada depois fim do bloco “try/catch/finally”
78. O Cerne de Boas Funções 3.5 Funções
Evite Repetição (DRY)
- Indireção, extração etc podem gerar duplicação
- Funções menores e especializadas requerem reúso (SRP)
Faça e Refaça com Confiança
- Comece fazendo funcionar e depois refatore
- Tenha cobertura de testes para ter confiança
Funções são verbos do domínio
- Uma hierarquia de funções conta uma estória
- Níveis de abstração ajudam na leitura da estória
80. Filosofia sobre Objetos e Estruturas de Dados 3.6 Objetos e E. de Dados
Os objetos usam abstrações para esconder seus
dados e expõem funções que operam sobre eles
Estruturas de dados expõem seus dados e não
possuem funções significativas (get/set = acessores)
- Objetos buscam o menor conhecimento
- Estruturas de dados (DTO) buscam imutabilidade
81. Estruturas de Dados nas Linguagens 3.6 Objetos e E. de Dados
Kotlin tem uma palavra reservada para definir uma estrutura
de dados: “data”. A sintaxe deixa clara a diferença entre
uma estrutura de dados e demais classes/objetos
84. Pedido Com Mais Encapsulamento 3.6 Objetos e E. de Dados
Objeto ou Estrutura de Dados?
85. Feature Envy (Inveja de Funcionalidade) 3.6 Objetos e E. de Dados
https://martinfowler.com/bliki/TellDontAsk.html
Um objeto tem “inveja” da funcionalidade de
que deveria ser implementada em outro objeto
No exemplo abaixo, o primeiro “IF” tem inveja da funcionalidade
que diz se um pedido é válido, mas pedido é um OBJETO
Numa ESTRUTURA DE DADOS isso não seria problema
Princípio “Tell
Don’t Ask”
Indireção
Sobre uma Estrutura de Dados
Sobre um Objeto
86. Procedural x Orientado a Objetos (OO) 3.6 Objetos e E. de Dados
E se precisar calcular o perímetro?
(as estruturas de dados não serão afetadas)
E se precisar criar a classe triângulo?
87. Espectros de Objetos / Estrutura de Dados 3.6 Objetos e E. de Dados
Encapsulamento
Estrutura
de Dados ObjetosHíbridos
Imutabilidade
Facilita a adição de novas funcionalidades
Facilita a adição de novas classes Triângulo
Perímetro
88. Feature Envy no Modelo de Figuras (Shape) 3.6 Objetos e E. de Dados
89. Feature Envy + Efeitos Colaterais 3.6 Objetos e E. de Dados
90. Lei de Demeter sobre Objetos 3.6 Objetos e E. de Dados
x = a.getB().getC().getD()x = a.b.c.d
Train Wrecks
(Acidente de Trem)
a
b c d
NullPointer
Exception
(null)
Estrutura de Dados Objetos
- Imutabilidade
- Proibição de “null”
- Estados mutáveis
- Possibilidade de “null”
x = a?.b?.c?.d
(Navegação Segura)
92. Filosofia sobre Tratamento de Erro 3.7 Tratamento de Erros
- Use exceções em vez de retornar códigos
- Prefira exceções não verificadas (unchecked)
- Forneça exceções com contexto e sem abafamento
- Pense no design do “try-catch-finally” antes
- Defina exceções pensando no chamador (e.g. Libs)
- Cada “catch” faz apenas uma coisa
- Não retorne nem passe “null”
- Dê preferência às exceções já fornecidas (efective java)
93. Código de Exceção Ruim 3.7 Tratamento de Erros
Problemas
- Não retorna uma exceção
- Abafa exceção do Gson
- Retorna “null”
- Imprime direto no console
Consequências
- Contrato baseado em “null”
- Omissão do erro raiz (causa)
- Força a testar nulidade:
kotlin: ?.
java: if (result != null)
- Log inadequado e ineficiente
94. Código de Exceção com Perda da Causa Raiz 3.7 Tratamento de Erros
Problemas
- Não retorna uma exceção
- Abafa exceção do Gson
- Retorna “null”
- Imprime direto no console
Consequências
- Contrato baseado em “null”
- Omissão do erro raiz (causa)
- Força a testar nulidade:
kotlin: ?.
java: if (result != null)
- Log inadequado e ineficiente
95. Código Melhorado (Repassando a Causa Raiz) 3.7 Tratamento de Erros
O que acontece se “json” ou “clazz” forem nulos?
Por que nunca se deve passar “null” como parâmetro?
96. Código de Teste Obtendo a Causa Raiz (Gson) 3.7 Tratamento de Erros
97. Questão Sobre a Criação de Classes de Exceção 3.7 Tratamento de Erros
Existe realmente a necessidade de criar a exceção “JsonException”?
Não seria melhor usar as exceções prontas do Gson?
99. Filosofia sobre Fronteiras 3.8 Fronteiras
- Código de terceiro quase sempre é “infraestrutura”
- Infraestrutura deve ser isolada do negócio
- Testes de aprendizado são uma boa dica
100. Estudo de Caso do Parsing de Json 3.8 Fronteiras
Necessidade
Fazer parsing e serialização de Json
em várias partes da aplicação
Inicialmente usando o “Gson”
Opcionalmente pode ser usado outra
biblioteca como “Jackson”
A infraestrutura deve ser isolada
Domain
ControllerController
JsonParser
Interface
toJson()
fromJson()
JsonParser
Interface
toJson()
fromJson()
GsonParserGsonParser
JacksonParserJacksonParser
101. Estudo de Caso do Parsing de Json 3.8 Fronteiras
Terceiros Infraestrutura
Infraestrutura
Aplicação
API de Terceiros
102. Estudo de Caso do Parsing de Json 3.8 Fronteiras
GsonParserGsonParser
JacksonParserJacksonParser
103. Parser Baseado no Gson 3.8 Fronteiras
GsonParserGsonParser
Encapsula o Gson
É um JsonParser
104. Injetando Dependência 3.8 Fronteiras
Criação manual
Injeção de dependência
Controller
Parser
Interface
toJson()
fromJson()
GsonParser
JacksonParser
107. Filosofia sobre Testes de Unidade 3.9 Testes de Unidade
- Teste de unidade dá feedback sobre o design
- O feedback deve chegar o quanto antes
- TDD é uma boa opção para tornar mais fácil
- Testes de unidade são atividade de construção
- Cobertura de testes provê confiança
- Refatoração sem cobertura pode ser uma aventura
- Teste também é código e deve ser limpo
108. Cenário de Construção com TDD em 3 Iterações 3.9 Testes de Unidade
Requisitos do Contador de Constantes
1. Criar uma função que conte todas as constantes inteiras de uma
classe passada como parâmetro (atributos estáticos e “final”)
2. Parâmetro “null”, classes abstratas e enuns são ignorados e
deve ser retornado “-1” como resultado da função
3. A contagem deve incluir todas as superclasses
109. As 3 Regras do TDD 3.9 Testes de Unidade
Refatora
Falha
Passa
1. Você não pode escrever nenhum código até ter escrito um
teste que detecte uma possível falha.
2. Você não pode escrever mais testes de unidade do que o
suficiente para detectar a falha – não compilar é não ter
efeito.
3. Você não pode escrever mais código do que o suficiente
para passar nos testes.
110. TDD – Escrever a Primeira Versão do Teste e Falhar 3.9 Testes de Unidade
Falha
1. Criar uma função que conte
todas as constantes inteiras de
uma classe (estáticos e “final”)
111. TDD – Escrever o Código Real e Passar 3.9 Testes de Unidade
Passa
112. TDD – Refatorar o Código Real e Passar (Apenas Melhoria) 3.9 Testes de Unidade
Passa
Extração da lista
Extração do método
113. TDD – Refatorar o Teste para o Requisito 2 (Iteração 2) 3.9 Testes de Unidade
2. Classes abstratas e enuns são ignorados (retornar -1), inclusive “null”
Refatora
114. TDD – Teste Refatorado Vai Falhar 3.9 Testes de Unidade
Falha
Falha Aqui
115. TDD – Ajuste do Código para Atender o Teste (Iteração 2) 3.9 Testes de Unidade
Passa
Adição do requisito 2
116. TDD – Refatorar o Teste para o Requisito 3 (Iteração 3) 3.9 Testes de Unidade
3. A contagem deve incluir as superclasses
Refatora
117. TDD – Teste Refatorado Vai Falhar (Iteração 3) 3.9 Testes de Unidade
Falha
Tem 2 constantes
Falha Aqui
118. TDD – Ajuste do Código para Atender o Teste (Iteração 3) 3.9 Testes de Unidade
Passa
Adição do requisito 3
120. Filosofia sobre Classes 3.10 Classes
- Classes devem ser pequenas
- Devem ter somente uma responsabilidade (SRP)
- Ter apenas um motivo para mudar
- Devem ter coesão interna*
* Poucas variáveis de instância
* Cada método manipula uma variável ou mais
- Devem encapsular seus dados ao máximo
- Devem ter baixo acoplamento a outras classes
121. Jogando “Pedra, Papel e Tesoura” 3.10 Classes
Requisitos do Melhor de 3 (Pedra, Papel e Tesoura)
1. Criar uma simulação de dois jogadores jogando “pedra, papel e
tesoura” por um número variável de rodadas (3, 5 etc).
2. O jogo deve parar se um jogador já for o vencedor antes da última
rodada (cenário de vitória antecipada).
3. Ao final deve ser indicado o vencedor (ou empate) e listados os
resultados de todas as rodadas.
122. Código Fornecido 3.10 Classes
Enum
Mão: Possibilidades de
escolha (Pedra, Papel ou
Tesoura)
123. Código Fornecido 3.10 Classes
Enum
Resultado: Resultado em relação
ao primeiro jogador:
Perde: -1
Empata: 0
Vence: 1
Abstração de pedraPapelTesoura
124. Código Fornecido 3.10 Classes
Enum
ResultadoJogada: Resultados
de um match (Primeiro Vence,
Empate ou Segundo Vence)
125. Implementação Procedural Simples 3.10 Classes
pedraPapelTesoura(char,char):int
Problemas
- Um método faz tudo
- Muitos conceitos juntos:
“Jogador”, “Match”, “Resultados”
- “Rodadas” constantes
- Nomes “hard coded”
- Não pára em vitória antecipada
Questões
Como se livrar do código legado
pedraPapelTesoura ?
A solução é muito procedural?
Como testar isso?
2 escolhas aleatórias
126. Modelo Lógico: Possível Solução OO 3.10 Classes
Níveis de Abstração
- O Jogo e os resultados
- Uma Disputa entre jogadores
- O resultado de uma disputa
- Jogador com nome e escolha
- A opção de jogada
127. Modelo Lógico: Análise dos Conceitos do Jogo 3.10 Classes
Inicialização e Contexto
Conceito: 1 JogadorConceito: 1 Disputa
Conceito: O Jogo em si
Várias rodadas e resultado
Código “Legado”: Lógica do jogo
Conceito: Resultado de uma Disputa
Abstração sobre -1,0,1
Opções do Jogo: R, P e S
128. Implementação OO - Main 3.10 Classes
Instanciação de
dependências
Inicialização
em “jogar()”
I/O = Efeito colateralI/O = Efeito colateral
129. Classe MelhorDeTres 3.10 Classes
Responsabilidade: representa um jogo de
“Pedra, Papel e Tesoura” com a definição
do número de rodadas (3 por padrão) e
armazena os resultados (estado)
API
- Executa o jogo (play)
- Retorna se tem vencedor
- Retorna o vencedor (se houver)
- Retorna os resultados
Delega para a classe Match o
conhecimento sobre a disputa entre dois
jogadores e seu resultado
Indireção para Jogada
Requisito número 2
Dependência será resolvida
via construtor
130. Classe JogadaLegada (Implementa Jogada) 3.10 Classes
Representa uma disputa entre dois jogadores e seu resultado (imutável)
API: Executa a disputa (play) e gera um resultado
Delega para os jogadores a escolha da “mão” que será jogada. Delega para a função
rockPaperScissors a lógica de quem venceu a disputa com base nestas escolhas
131. Classe Jogador 3.10 Classes
Representa um jogador que deve fazer uma escolha de qual mão usar na disputa
Delega para Mao a escolha de forma aleatória, mas encapsula esta decisão
O jogador poderia ser criado com a escolha já feita anteriormente
132. Solução OO x Procedural 3.10 Classes
A solução OO tem mais código, mas cada parte é mais fácil de entender que o todo
Permite testes de unidade por classe
Cada abstração está em uma classe
Cada classe tem uma responsabilidade
A necessidade de manutenção tende a impactar somente uma parte do código
133. Refatoração: Livrando-se do Código Legado 3.10 Classes
Como remover pedraPapelTesoura?
Qual classe deve conhecer esta regra
de quem vence quem?
Como implementar a regra de forma a
gerar o menor impacto possível?
Qual seria o impacto de alterar e
testar na solução procedural?
136. Responsabilidade da Classe “Mao” 3.10 Classes
Código expressivo sobre a regra do jogo
Função (API) que externaliza a regra
137. Impacto: Nova Classe Implementando “Jogada” 3.10 Classes
Implementação alternativa de “Jogada” livre do código legado
O método “jogar” expressa o negócio de forma simples e elegante
Função (API)
que externaliza
a regra
138. JogadaLegada x JogadaSimples (Nova) 3.10 Classes
- Mais semântico e claro
- Dispensa o enum “Resultado”
- Delega para o método “vence” de
uma outra classe (“Mao”)
Kotlin infix
139. Impacto da Refatoração na Classe “Main” 3.10 Classes
Criação da classe “JogadaSimples” (Troca de “JogadaLegada” por “JogadaSimples”)
A classe “MelhorDeTres” não teve nenhum impacto e ainda recebe a dependência
Responsabilidades:
- Criar Jogadores
- Criar Jogada
- Inicializar o Jogo
...
- Conexão de Banco ?
- I/O, Integrações ?
141. Filosofia sobre Testes de Unidade 3.11 Sistemas Complexos
- Sistemas precisam de separação de preocupações
- Separe a inicialização do uso de um sistema
- Sistemas beneficiam-se do uso de uma DSL
142. Modularização e Separação de Preocupações 3.11 Sistemas Complexos
- Criação de objetos e escopos superiores
- Conexão com BD, I/O e efeitos colaterais DDD e Preocupações Transversais
Main main()
Negocio
Módulo A
Domain Service
Módulo B
Função X1
Classe X Função X2
Função X3
Factory Create
Módulo C
Função Y1
Classe Y Função Y2
Função Y3
Repository Save
Log
Segurança
BD
I/O
143. Inicialização e Contexto
Conceito: 1 JogadorConceito: 1 Disputa
Conceito: O Jogo em si
Várias rodadas e resultado
Código “Legado”: Lógica do jogo
Conceito: Resultado de uma Disputa
Abstração sobre -1,0,1
Opções do Jogo: R, P e S
Separe a Construção do Uso no Sistema 3.11 Sistemas Complexos
As demais classes não
devem conhecer sobre
contexto e inicialização
Devem ser projetadas
como se isso fosse ser
resolvido para elas…
E será!
Main conhece o sistema
Sistema ignora o Main
Função pura ignora tudo
fora do seu escopo
144. Injeção de Dependência Via Construtor 3.11 Sistemas Complexos
O uso de CDI com
@inject faz o mesmo
trabalho automaticamente
Injeção de dependência minimiza o
acoplamento entre classes além de
facilitar os testes de unidade
145. DSL – Domain Specific Language 3.11 Sistemas Complexos
DSL: Código
Expressivo
DSL encapsula a lógica
Main main()
Negocio
Módulo A
Domain Service
Módulo B
Função X1
Classe X Função X2
Função X3
Factory Create
Módulo C
Função Y1
Classe Y Função Y2
Função Y3
Repository Save
Classes e Funções são negócio destilado
O domínio do problema é descrito na solução
147. Filosofia sobre Simplicidade 3.12 Simplicidade
- Todos os testes devem passar (deve haver testes)
- Nenhuma duplicação de código ou configuração
- O código deve expressar o negócio e sua intenção
- A solução deve usar o mínimo de elementos
- Simples não é Simplório (pelo contrário)
- Simplicidade é a sofisticação máxima
148. As 4 Regras da Simplicidade 3.12 Simplicidade
Testes Tempestivos
Refatoração Tempestiva
https://martinfowler.com/bliki/BeckDesignRules.html
149. Regra 1 de Projeto Simples: Efetue Todos os Testes 3.12 Simplicidade
1 2
3
Teste 1: Afere se a regra entre as opções
está funcionando: Pedra vence Tesoura,
Tesoura vence Papel e Papel vence Pedra
Teste 2: Afere se o SmartMatch retorna o
resultado certo dado que já se sabe o que
os jogadores vão escolher (mock)
Teste 3: Afere que, de 7 rodadas, irá parar
na quarta com 4 vitórias de “Bob”
150. Regras de 2 a 4 de Projeto Simples: Refatoração 3.12 Simplicidade
Conceitos
- O Jogo e os resultados
- Uma Disputa entre jogadores
- O resultado de uma disputa
- Jogador com nome e escolha
- A opção de jogada
DSL: Código
Expressivo
A: Reúso
B: Refactoring
Regra
1 conceito p/ classe
151. já sei clean code?
técnicatécnica, treino e tempo, treino e tempo
abordagem de atuação profissional
não é sobre códigonão é sobre código
é sobreé sobre fazer códigofazer código
154. REFERÊNCIAS
BECK, Kent. Padrões de Implementação. Porto Alegre, Bookman, 2013.
EVANS, E. Domain-Driven Design – Atacando as complexidades no coração do software. 2a edição, Rio de
Janeiro, Alta Books, 2010.
FOWLER, Martin. BeckDesignRules, https://martinfowler.com/bliki/BeckDesignRules.html
FOWLER, Martin. TellDontAsk, https://martinfowler.com/bliki/TellDontAsk.html
HUNT, Andrew; THOMAS, David, O Programador Pragmático, Bookman, 2010
MARTIN, Robert, O Codificador Limpo, Alta Books, Rio de Janeiro 2012
MARTIN, Robert, Código Limpo, Alta Books, Rio de Janeiro 2011
155. Programação Funcional X Orientação a Objetos
155/155
1 42 3 5
Prática
para encerrar...
Obrigado!
Douglas Siviotti
douglas.siviotti@gmail.com
github.com/siviotti
Teoria