Skip to content

Instantly share code, notes, and snippets.

@lsfernandes92
Last active August 26, 2021 17:20
Show Gist options
  • Save lsfernandes92/1cb831984fd1a2a024eb4d9cb3f0c935 to your computer and use it in GitHub Desktop.
Save lsfernandes92/1cb831984fd1a2a024eb4d9cb3f0c935 to your computer and use it in GitHub Desktop.
Clean code
* Capítulo 13: Simultaneidade
- O fato de ter várias coisas fazendo
ao mesmo tempo, ao inves de fazer uma
por uma e não economizar tempo de request
- Não queremos que em um site de web
scrapper, por exemplo, não faça uma request
a cada vez que ele for procurar algo na
página
- Ter o código pensando de tal forma
ajuda a manter ele mais organizado e
seguindo conceitos do SRP
- É como separar o processamento em vários
computadores e não deixar um processamento
unico em um looping
- Do ponto de vista estrutural deixar a
aplicação mais organizada, ou seja,
todo parte de concorrencia é tratada
em algum modulo especifico. Do ponto
de vista de performance deixa a
aplicação com menos tempo de espera,
melhor respostas quando colocado sob
stress, e tals...
* Crenças e equívocos
- Mexer com problemas de concorrencia
pode ou não melhorar o desempenho. Considere
o exemplo de um aplicação que está com mt
tempo de resposta
- Melhorar problema de concorrencia
geralmente vai ter que ter uma reestruturação
do código
- É sempre mt dificil investigar esse
problema, pois são mts possibilidades de
caminhos que o código pode percorrer em
uma aplicação multi-thread
* Mecanismos de defesa pra/com código
de simultaniedade
- Abusar do conceito de SRP. O código
que trata de concorrência já tem suas
mais que razões pra mudar, por isso
seria legal separa-lo. Recomendação:
Separe seu código de concorrencia sempre
* Thread-safe ref:
- https://pt.wikipedia.org/wiki/Thread_safety
- https://www.devmedia.com.br/thread-safe-java-entendendo-o-conceito-e-usando-em-sua-aplicacao/28858
* Thread safe
- Conceito que faz com que uma thread utilize de
forma compartilhada os seus dados sem interferir, ou
deixar inconsistencias em outras threads em andamento
* Continuação capítulo 13 sobre
simultaniedade(mecanismos de defesa)
- Ter consciencia e fazer com que as thread sejam
organizadas e executadas de maneira unica sem
atrapalhar outras thread ou deixar inconsistencias
- Conhecer suas bibliotecas. Ou seja saber usar
as mesmas que usam de conceitos de thread-safe
- Conceitos básicos para entender programação simultanea:
1. Exclusão mutua: Quando uma thread não interfere
na execução de outra thread
1. Starvation: Acontece quando uma thread que é considerada
mais rápida para executar é colocada na frente
na fila por motivos óbvios e, posteriormente,
essas mesmas threads demorar a executar. Em seguida threads
longas que esperam respostas dessas threads curtas
são interrompidas pois já esperavam a resposta dessa
threads curtas.
1. Livelock: Quando uma thread em andamento entra
em conflito com outra thread que tb está em andamento
1. Deadlock: Quando duas threads dependem uma da outra
para prosseguir. Basicamente oq acontece é essa
espera eterna entre as duas threads
- Modelos usado em programação concorrente
1. Produtor-consumidor: acontece quando existe um ou vários
produtores que assim que processam alguma coisa, coloca isso
em alguma fila ou buffer, notifica essa ação para que com
isso os consumidores fiquem cientes das notificações e
processem isso de maneira ordenada. Assim que os consumidores
fizerem isso eles tb lançam uma notificação avisando.
Esse tipo de comunicação por notificações é o pilar desse
tipo de programação.
2. Escritores-leitores: se baseia numa relação de leitura/escrita
onde oq deveria acontecer, ou de certa forma ser minimizada/balanceada,
é que quando alguém está lendo, que essa ação não seja interrompida
e vice versa para o caso da escrita. Esse tipo de progração pode
causar contra efeitos de deadlock ou livelock.
3. Jantar de filósofos: o exemplo é de: imagine uma mesa rodeada
de filósofos. A esquerda deles existem um garfo, eles estão
sentado em volta de uma mesa e no centro dela existe um bowl
com macarrão. A proposta é que eles devem refletir até que
sintam fome. Se sentirem fome eles devem comer somente com dois
garfos, porém se o filósofo do lado estiver usando o garfo
esse que iria comer deve esperar. No exemplo os filósofos
são substituidos por threads e o garfo por recursos.
- ESTEJE atento com o uso dos métodos sincronizados
e se possível usar somente em um lugar do objeto
que precisa dessa palavra chave
Caso não possível recorra a uma das três estratégias. São elas:
Client-Based Locking, Server-Based Locking ou Adapted Server.
- Utilize pouco do "synchronized" nos metódos. Aqui ele da o exemplo
de utilizar pouco dessa possibilidade encontrada na linguagem java
que é de utilizar da palavra "synchronized" para métodos que
queremos que tenha uma ação mais minusciosa e sem threads extras.
Porém o uso desse artefato deve ser bem pensado e não ser usado
de maneira absurda pois isso causa a degradação do throughput
do sistema. Recomendação: mantenha os métodos sincronizados pequenos
- ESTEJE atento para o correto momento de "desligar" partes
do sistema. A dica é: tenha em mente que isso é um tarefa
dificil, então pense nessa parte antecipadamente pq isso
provavelmente levará mais tempo que o necessário.
- Testando código que usam threads
Não ignorar testes que falham somente de vez em quando.
E algumas estratégias são:
- Manter os teste plugaveis, ou seja rodar em diversas
variações e configurações
- Resolver os bugs antes de qualquer coisa. Fazer com que
o código esteja funcionando adequado primeiramente antes
de partirmos para preocupações com threads
- Manter os testes "tunaveis", tentar arranjar uma
maneira com que vc consiga manipular as threads,
processadores, e tals...
- Rodar mais threads do que processadores
- Rodar o teste em várias plataformas, não mt a dizer, mas
diferentes plataformas tem suas diferentes formas de
tratar threads
- Forçar o código com métodos .sleep(), .wait() por exemplo.
Isso fará com que vc force seu código a aparecer com o erro.
Problemas: vc tem que saber exatamente a parte do código que
usará esse métodos,
* Capítulo 17 - Cheiros de código
O intuito aqui é só nomear alguns dos code smells apresentados.
Faz parte da minha maneira de tentar fixar algo que
estou estudando.
* Comentários:
1. Comentários inapropriados: que não diz respeito a coisas
técnicas ou de valor pra uma comentário. Não se preocupe
com isso pois o sistema de controle de versão está ali
pra isso
2. Comentário redundante: que simplesmente diz que aquela
parte do código é uma variavel ou simplesmente replica
o nome da variavel como comentário
3. Código comentado: é uma abominação! Se ver isso apague
e, novamente, deixe que o sistema de controle de versão
cuide disso. Se isso for realmente necessário já teria sido
descomentado
4. Comentário obsoleto: a dica novamente é: apague!
Comentários ficam obsoletos mt rápidos se não forem
implementados. Esse tipo de comentário é muito ruim,
pois pode levar a uma perda de tempo enorme rumando
vc que está tentando entender o código a uma direção
errada e atrapalhando seu conhecimento.
* Funções:
1. Função morta: uma função que não está sendo usada
é só apagar :)
2. Função com flag: muito ruim pois isso diz
claramente que aquela função está fazendo mais
de uma coisa. Partindo do principio que uma
função deveria fazer apenas uma coisa, isso
não deveria acontecer. Refatore para outra
função independente.
3. Função com muitos parametros: Função boa é
aquela com nenhum parametro. Nos demais casos tente
manter um número não superior a no máximo 3 parametros
passados para uma função. No demais refatore e tente
deixar aquilo aquela função mais coesa
* Gerais:
1. Comportamento óbvio não implementado: Isso faz
o leitor perder a confiançã no que ele espera
lendo uma função
2. Código de várias linguagens dentro de um unico
arquivo: tente evitar com que isso aconteça separandos
os arquivos
3. Duplication: tente sempre usar o conceito de DRY
por onde passe, pois isso deixa o código mais organizado,
menos propenso a erros e a abstração pode facilitar a vida
do amiguinho. Existe o claro exemplo de um código
duplicado, mas temos tb casos não tão óbvios, mas que
tb chamam a atenção, tipo: classes/módulos que fazem
coisas semelhantes e/ou if's ou switch's que sempre
aparecem em partes do código.
4. Inconsistencias: se vc começou a escrever o código
de uma forma tente usar o mesmo template nas linhas
seguintes. Por exemplo: nomeie variaveis de forma
semelhante. Coisas assim que deixam o código mais
fácil e melhor de ser lido
5. Separação vertical: mantenha códigos relacionados
próximos. Por exemploe: uma função privata loga abaixo
da função que está usando ela e variáveis locais no
começo de um método
6. Código morto: apague né, mores?!
7. Clutter: basicamente fala que não faz sentido ter
coisas que não estão sendo usadas, exemplo: métodos,
comentários, classes, etc...
8. Feature Envy: quando operações em um objeto
é feita dentro de outro objeto, deixando claro assim
que esse outro objeto não está devidamente encapsulado
9. Selector argument: Uma outra forma de dizer que
argumento flagueados não são uma boa idéia e nada mais
que uma maneira preguiçosa de dividir os métodos
10. Obscure intent: fala principalmente de nomes de
variaveis usando siglas, ou usando a conotação hungariana,
etc...
11. Artificial coupling: quandos dois objetos fazem
interações, mas que não fazem mt sentido esses dois
terem uma relação entre si, ou seja, parece uma forma
preguiçosa de ligar as coisas
12. Nomes de funções tem que expor oq elas fazer.
De nada adianta ter uma função de nome `Date.add(6)`
se não sabemos oq esse add está fazendo. Ela está
adicionando dias? Está adicionando horas? Está adicionando
minutos? Está criando uma nova insntancia de data ou
está somando com a data atual? Esse tipo de pergunta
devemos fazer ao olhar o nome de uma função
13. Escolher bem onde colocar as responsabilidades:
Acontece que muitas vezes entramos em um paradóxo
de tentar achar o melho lugar para colocar certas coisas.
14. Usar nomes de variaveis auto explanatórias: sem siglas e
sem abreviações
15. Entenda o código: o simples fato de usar if já demonstra
a força bruta que teve ao programar. Não há nada de errado
em usar if's, porém posteriormente tente entender e fazer
as abstrações corretas
16. Use polimorfia em vez de if/switch:
17. Use constantes para números mágicos: não todos, mas tente
ser crítico e nomear números soltos pelo código
18. Siga convenções do time: não importa se irá usar espaços ou
tabs para formatar seu código, contanto que vc concorde com o
que o time já usa em prática
19. Use dependencias fisicas e não lógicas: como por exemplo,
um número fixado para gerar páginas de relatórios. Esse tipo
de variavel deveria ser obtido apartir de um classe de "Reporter"
ou ao do tipo. Apenas de forma mais dinamica e não hard-coded
20. Seja preciso: tente escolher bem as coisas e ter uma lógica
do pq feito daquele jeito
21. Encapsular lógicas de if: pelo simples fato de if demandarem
um mapa mental na hora de ler o código
22. Tente não usar if negados: pelo simples fato de if demandarem
um mapa mental na hora de ler o código
23. Métodos devem fazer apenas uma coisa: SRP feelings
24. Tente expressar bem a ordem de chamar os métodos: se funções
devem ser chamadas em determinada ordem faça com isso seja
frisado de certa forma. Exemplo:
```
Gradient gradient = saturateGradient();
List<Spline> splines = reticulateSplines(gradient);
diveForMoog(splines, reason);
```
25. Encapsular condições de limite: coisa do tipo `level + 1`
deveria ser jogada em uma variável do tipo `next_level = level + 1`
26. Não misture níveis de abstrações
27. Mantenha constantes usado pelo sistema em um lugar só
28. Lei do Demeter: Faça com que um objeto não "converse"
com estranhos. Exemplo: uma chamada do tipo `a.getB().getC()`
dificulta mt a mudança em uma sistema pq qualquer mudança no
meio do caminho nesses objetos, dificultaria a mudanças em
lugares do sistema que usa esse tipo de acesso
* Nomes:
1. Use nomes descritivos: o caso aqui é não usar siglas
ou palavra abreviadas. Use nome descritivos que explique
bem oque aquela variavel ou nome de função está fazendo
2. Não use nomes equivocadamente: nomeie algo que realmente
diga oq ele ta fazendo. Não tente omitir parte do que uma
função está fazendo na sua internalidade.
3. Use nome padrões quando der: exemplos disso é quando
se é usado uma pattern, exemplo: ImageFactory, OrderController,
e etc... Outro exemplo disso é quando o seu time no qual
está trabalhando usa certas nomenclaturas no código. Tente
usar isso ao máximo tb pq quanto mais lugar isso aparecer
no código, melhor o leitor irá se acostumar com o que isso
representa
4. Escolha nomes no mesmo nível de abstração: não tente
especializar muito quando nomeia alguma coisa (?). Tente
por exemplo abstrair aquele nome e não deixar com um nome
a tão baixo nível
5. Não esconta efeitos colaterais de uma coisa nomeadada:
Por exemplo uma função que retornar cria alguma coisa chamada
"oos" e retorna esse mesmo "oos" o nome dessa função deveria
se chamar `create_or_return_oos` e não somente `return_oos`
6. Quando maior o escopo maior o nome da váriavel: por exemplo:
tudo bem o nome de uma variavel chamar "j" ou "i" dentro
de um looping(principalmente em Java). Porém, o nome de
um método ou classe deveria ser mais descritivo que isso
7. Não use notações: não use os chamados encoding, por exemplo,
prefixos do tipo "m_", "f", "stringNomeVariavel",
"booleanNomeVariavel", etc... Isso hj em dia não é preciso
se preocupar principalmente com linguagen não tipadas.
* Testes:
1. Teste o suficiente: Se alguma coisa pode possivelmente
quebrar ou retornar algo inesperado, teste! Não deixe
de cobrir esses casos
2. Use alguma ferramente de cobertura de testes: eles
podem ajudar a vc encontrar testes de paths que não
foram pensados ou passaram despercebidos
3. Teste bem os limites
4. Não deixe de fazer os teste triviais: eles são importantes
e custam pouco pra fazer e de muito valor serve sua
documentação
5. Um teste ignorado pode ser uma questão que não está
esclarecida mt bem pelos requisitos da funcionalidade
6. Testes excessivamente em partes que existem bugs:
geralmente mais bugs são descobertos ao fazer esse
simples fato
7. Teste são na maioria das vezes reveladores de
problemas. Por exemplo, quando algum bug ou algo
acontece de errado o teste pode servir de um guia
mt bom para uma análise inicial
8. Looking at the code that is or is not executed
by the passing tests gives clues to why the
failing tests fail.
9. Mantenha a execução dos teste rápida
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment