No da semana passada, explicamos o que era código limpo, qual a sua importância e os principais problemas que ocorrem quando ele não é simples e objetivo. Hoje traremos diversos conceitos que visam demonstrar COMO deixar o seu código limpo!
Uma observação, as linhas de código que mostraremos durante o curso terão duas cores, vermelho e verde. As que estão em vermelho significa que existem espaços de melhoria e as em verde, as mudanças que incorporam as sugestões de boas práticas. Sem mais perda de tempo, vamos aos conceitos!
Nomes com significado
Código Limpo
Nomes com significado
Nomes estão por toda parte no software, onipresentes. Nomeamos nossas variáveis, funções, argumentos, classes e pacotes. Nós nomeamos arquivos de origem e diretórios. Nós nomeamos nossos arquivos jar, war e outros.
Nós nomeamos e nomeamos e nomeamos.
Usar nome com intenções
Escolher bons nomes leva tempo, mas economiza trabalho. O nome de uma variável, função ou classe deve responder a todas as perguntas importantes. Ele deve dizer-lhe por que ele existe, o que ele faz, e como ele é usado. Se um nome requer um comentário, então o nome não revela sua intenção.
int d; // tiempo transcurrido en días
int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;
Evite desinformação
Os programadores devem evitar deixar pistas falsas que obscureçam o significado do código. Devemos evitar palavras cujos significados arraigados diferem do nosso significado pretendido.
accountList XYZControllerForEfficientHandlingOfStrings
int a = l;
if ( O == l )
a = O1;
else
l = 01;
Fazer distinções significativas
A nomeação de séries de números (a1, a2, .. aN) é o oposto de nomeação intencional. Tais nomes não são desinformação, não são informativos; eles não dão nenhuma pista sobre a intenção do autor.
public static void copyChars(char a1[], char a2[]) {
for (int i = 0; i < a1.length; i++) {
a2[i] = a1[i];
}
}
Esta função é muito melhor quando você usa a fonte e o destino para nomes de argumentos.
Se faça algumas perguntas antes de definir as distinções.
- O que você deve entender como uma distinção?
- Qual representará o melhor caminho para o histórico de pagamentos de um cliente?
- Como os programadores deste projeto devem saber qual dessas funções chamar?
Use nomes pronunciáveis e pesquisáveis
Se você não pode pronunciá-lo, você não pode discutir com ele sem soar como um. “Bem, aqui no bee cee arr três cee enn tee temos um xixi zee kyew int, vê?” Isso é importante porque a programação é uma atividade social.
Nomes de letras únicas e constantes numéricas têm um problema particular, pois não são fáceis de localizar em um corpo de texto.
for (int j=0; j<34; j++) {
s += (t[j]*4)/5;
}
to
int realDAysPerIdealDay = 4;
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for (int j=0; j < NUMBER_OF_TASKS; j++) {
int realTaskDays = taskEstimate [ j ] * realDAysPerIdealDay;
int realTaskWeeks = ( realdays / WORK_DAYS_PER_WEEK ) ;
sum += realTaskWeeks;
}
Evite o mapeamento mental
Certamente, um contador de loop pode ser chamado de i ou j ou k (embora nunca l!) No entanto, na maioria dos outros contextos, um nome de letra única é uma má escolha;
r = 8;
Uma diferença entre um programador inteligente e um programador profissional é que o profissional entende que a clareza é o que importa. Os profissionais usam seus poderes para o bem e escrevem códigos que outros podem entender.
Nomes de classe
Classes e objetos devem ter nomes ou frases como Cliente, WikiPage, Conta e EndereçoParser. Evite palavras como Administrador, Processador, Dados ou Informações em um nome de classe. O nome de uma classe não deve ser um verbo.
Nomes do método
Os métodos devem ter nomes de verbo ou frases verbo, como postPayment, deletePage ou save. Os descritores de acesso, modificação e predicado devem ser nomeados pelo seu valor e com o prefixo obter, definir e está de acordo com o padrão javabean.
Não faça trocadilhos
Evite usar a mesma palavra para dois propósitos. Usar o mesmo termo para duas ideias diferentes é essencialmente um jogo de palavras. Suponha que tenhamos muitas classes em que adicionar criará um novo valor adicionando ou concatenando dois valores existentes. Agora vamos dizer que estamos escrevendo uma nova classe que tem um método que coloca seu único parâmetro em uma coleção.
Devemos chamar esse método de adicionar? Pode parecer consistente porque temos muitos outros métodos de adição, mas neste caso, a semântica é diferente, então devemos usar um nome como inserir ou adicionar em vez disso. Chamar o novo método de adicionar seria uma brincadeira com palavras.
Use nomes de domínio de solução
Lembre-se que as pessoas que lerem seu código serão programadores. Então vá em frente e use termos de ciência da computação (CS), comes de algoritmos, nomes de padrões, termos matemáticos, etc. É imprudente extrair todos os nomes de domínio do problema porque não queremos que nossos colegas de trabalho tenham que ir e vir até o cliente perguntando o que cada nome significa quando eles já conhecem o conceito com um nome diferente.
Adicionar contexto significativo
Existem alguns nomes que são significativos em si mesmos, a maioria não. Em vez disso, você deve colocar nomes no contexto para o seu leitor, bloqueando-os em classes, funções ou namespaces bem nomeados. Quando tudo mais falhar, pode ser necessário pré-definir o nome como um último recurso.
Imagine que você tem variáveis nomeadas firstName, lastName, street, houseNumber, city, state e zipcode. Juntos, é bastante claro que eles formam uma direção. Mas e se você visse que a variável estado é usada sozinha em um método? Você inferiria automaticamente que era parte de um endereço? Você pode adicionar contexto usando prefixos: addrFirstName, addrLastName, addrState, etc. Pelo menos os leitores entenderão que essas variáveis fazem parte de uma estrutura maior. Claro, uma solução melhor é criar uma classe chamada Address. Assim, até o compilador sabe que as variáveis pertencem a um conceito mais amplo.
FUNÇÕES
-
- A primeira regra das funções é que elas devem ser
- A segunda regra das funções é que elas devem ser
- Quão curta deve ser uma função?
Funções
Código Limpo
- A primeira regra das funções é que elas devem ser
- A segunda regra das funções é que elas devem ser
- Quão curta deve ser uma função?
Blocos e recuo
Os blocos dentro das declarações se, outras etc. Eles devem ser uma linha de comprimento. Provavelmente essa linha deve ser uma chamada de função. Isso também implica que as funções não devem ser grandes o suficiente para conter estruturas aninhadas, o nível de recuo de uma função não deve ser maior do que um ou dois. Isso, é claro, torna as funções mais fáceis de ler e entender.
Faça uma coisa
Deve ser muito claro que a longa lista do exemplo anterior faz muito mais do que uma coisa. Trata-se de criar buffers, recuperar páginas, procurar páginas legados, renderizar caminhos, adicionar strings antigas e gerar HTML, entre outras coisas. Ele está muito ocupado fazendo muitas coisas diferentes. Por outro lado, o programa abaixo, está fazendo uma coisa simples. Inclui configurações e desmontagem em páginas de teste.
- Determine se a página é uma página de teste.
- Se assim for, incluindo configurações e desmontagem.
- Renderizar a página em HTML.
Um nível de abstração por função
Para ter certeza de que nossas funções estão fazendo “uma coisa”, devemos ter certeza de que as afirmações dentro de nossa função estão todas no mesmo nível de abstração. Misturar níveis de abstração dentro de uma função é sempre confuso. Saiba como diferenciar se uma determinada expressão é um conceito essencial ou um detalhe. Uma vez que os detalhes são misturados com conceitos essenciais, mais e mais detalhes tendem a se acumular dentro da função.
Leitura de código de cima para baixo, a regra de redução
Queremos que o código leia como uma narrativa de cima para baixo. Queremos que cada função seja seguida por aquelas no próximo nível de abstração para que possamos ler o programa, descendo um nível de abstração ao mesmo tempo durante a leitura da lista de funções. Isso se chama Regra de Redução.
Instruções de comutação
É difícil fazer uma pequena função usando a instrução do switch. Então é difícil fazer uma instrução de troca que faz apenas uma coisa. Por sua natureza, trocar instruções sempre fazem n coisas.
Infelizmente, nem sempre podemos evitar instruções de switch, mas podemos garantir que cada declaração de mudança seja enterrada em uma classe de baixo nível e nunca repetida. Fazemos isso, é claro, com polimorfismo.
-
- É grande e quando novos tipos de funcionários são adicionados, ele vai crescer.
- Ele claramente faz mais de uma coisa.
- Viola o Princípio da Responsabilidade Exclusiva (SRP) porque há mais de uma razão para que ele mude.
- Viola o princípio aberto fechado (OCP) porque deve mudar cada vez que novos tipos são adicionados.
- Mas possivelmente o pior problema com esse recurso é que há um número ilimitado de outras funções que terão a mesma estrutura.
isPayday (Employee e, Data date),
deliverPay (Employee e, Money pay),
A solução para este problema é ocultar a instrução do switch em uma FÁBRICA ABSTRATA, e nunca deixar ninguém vê-lo.
A fábrica usará a declaração de alteração para criar instâncias apropriadas dos derivativos do Funcionário, e as várias funções, como o calculatePay, isPayday e deliverPay, serão enviadas polimorficamente através da interface do Funcionário.
Argumento de função
O número ideal de argumentos para uma função: é zero,
Um ou dois. Três (tríades) argumentos devem ser evitados sempre que possível. Mais de três (poliádicos) requerem uma justificativa muito especial e não devem ser usados de qualquer maneira.
Os argumentos são difíceis. Eles exigem muito poder conceitual. Os leitores têm que interpretar cada vez que vêem. Os argumentos são ainda mais difíceis do ponto de vista das evidências.
Circle makeCircle (duplo x, duplo y, raio duplo);
Circle makeCircle (Centro de ponto, raio duplo);
Não tem efeitos secundários
os efeitos colaterais são trapaças, enganos, mentiras. Sua função promete fazer uma coisa, mas também faz outras coisas ocultas.
Às vezes, você fará mudanças inesperadas nas variáveis de sua própria classe. Às vezes, ele os converterá nos parâmetros passados para a função ou para os globais do sistema. De qualquer forma, são falsidades tortuosas e prejudiciais que muitas vezes resultam em estranhos acoplamentos temporais e dependências da ordem.
Verbos e palavras-chave
Escolher bons nomes para uma função pode percorrer um longo caminho para explicar a intenção da função e a ordem e intenção dos argumentos.
No caso de um mônade,
- a função e o argumento devem formar um par de verbo/substantivo muito bom.
- Por exemplo, write(nome) é muito evocativo.
- Seja qual for o“nome”,estásendo
- Um nome ainda melhor poderia ser writeField (nome), que nos diz que o “nome” é um “campo”.
Gostaram das dicas para deixar o seu código limpo? Se quiser ficar por dentro de mais dicas como essa se cadastrem na nossa newsletter!