5 regras de código fáceis de ler

Você se pergunta: "Como escrever de forma limpa e clara?" Como descobrir rapidamente o código de outra pessoa? "

Lembre-se das regras abaixo e aplique-as!

O artigo não discute as regras básicas para nomear variáveis ​​e funções, indentação sintática e um tópico de refatoração em larga escala. Consideramos 5 regras simples para simplificar o código e reduzir a carga no cérebro durante o processo de desenvolvimento.

Considere o processo de percepção de dados para correlacionar as regras descritas com o processo de percepção e determine os critérios para um código simples.

O processo simplificado de percepção consiste nas seguintes etapas:

  1. Os dados recebidos através dos receptores são consistentes com a experiência anterior.
  2. Se não houver correlação, isso é ruído. O barulho é rapidamente esquecido. Se há algo a que se relacionar, o fato é reconhecido.
  3. Se o fato for importante - lembramos, generalizamos ou agimos, por exemplo, dizemos ou digitamos o código.
  4. Para reduzir a quantidade de informações memorizadas e analisadas, a generalização é usada.
  5. Após resumir, as informações são novamente correlacionadas e analisadas (etapa 1).


Os cientistas dividem a memória em curto e longo prazo. A memória de curto prazo é pequena em volume, mas as informações são extraídas e armazenadas instantaneamente. Memória de curto prazo é o cache do cérebro. Pode armazenar 7 + -2 palavras, números, objetos. A memória de longo prazo é maior em volume, mas requer mais energia (esforço) para salvar e recuperar informações do que a memória de curto prazo.

Conclusões:

  • quanto menos elementos, menos energia é gasta,
  • necessário reduzir o número de elementos percebidos para 9,
  • use a memória de longo prazo o menos possível: generalize ou esqueça.

Prosseguimos com a descrição das regras.

Regra 1. Usamos a declaração nas condições, livramos-nos do "não".

A remoção do operador “not” reduz o número de elementos que estão sendo analisados. Além disso, em muitas linguagens de programação, um "!" É usado para o operador de negação. É fácil pular esse caractere ao ler o código.

Compare:

if (!entity.IsImportAvaible) { // 1 } else { // 2 } 

Depois:

 if (entity.IsImportAvaible) { // 2 } else { // 1 } 

Regra 2. Reduzindo o nível de aninhamento.

Durante a análise de código, cada nível de aninhamento precisa ser mantido na memória. Reduza o número de níveis - reduza o custo do combustível.

Considere maneiras de reduzir o número de níveis de aninhamento.

1) Controle de retorno. Cortamos alguns dos casos e focamos nos demais.

 if (conributor != null) { // } 

Converter em

 if(contributor == null) { return; } // 

ou

 while( 1) { if( 2) { break; } // } 

ou

 for(; 1;) { if( 2) { continue; } // } 

Um exemplo estendido é dado na regra 5. Observe o lançamento de uma exceção.

2) Isolamento do método. O nome da função é o resultado da generalização.

 if( ) { foreach(var sender in senders) { sender.Send(message); } } 

em

 if( ) { SendAll(senders, message); } void SendAll(IEnumerable<Sender> senders, string message) { foreach(var sender in senders) { sender.Send(message); } } 

3) condições de combinação.

 if (contributor == null) { if (accessMngr == null) { // } } 

em

 if (contributor == null && accessMngr == null) { // } 

4) Removendo variáveis ​​e dividindo em blocos semânticos. Como resultado, os blocos podem ser alterados independentemente e, o mais importante, é muito mais fácil ler e entender esse código. Um bloco - uma funcionalidade. Um caso comum é uma pesquisa com processamento. É necessário dividir o código em blocos semânticos separados: pesquisa de elementos, processamento.

 for (int i=0;i<array.length;i++) { if (array[i] == findItem) { // array[i] break; } } 

em

 for(int i=0;i<array.length;i++) { if(array[i] == findItem) { foundItem =array[i]; break; } } if (foundItem != null) { // foundItem } 

Regra 3. Nos livramos de indexadores e chamadas através de propriedades.

Indexador - a operação de acessar um elemento de uma matriz no índice arr [index].

No processo de percepção do código, o cérebro opera com os símbolos [] como delimitadores e o indexador como uma expressão.

 function updateActiveColumnsSetting(fieldsGroups) { var result = {}; for (var i = 0; i < fieldsGroups.length; i++) { var fields = fieldsGroups[i].fields; for (var j = 0; j < fields.length; j++) { if (!result[fieldsGroups[i].groupName]) { result[fieldsGroups[j].groupName] = {}; } result[fieldsGroups[i].groupName][fields[j].field] = createColumnAttributes(j, fields[j].isActive); } } return JSON.stringify(result); } 

Um indexador é um local de erros frequentes. Em virtude de nomes curtos de índice, é muito fácil misturar I, j ou k.

No exemplo acima, no resultado da linha [fieldsGroups [j] .groupName] = {}; erro cometido:
j é usado em vez de i.

Para descobrir onde exatamente o i-ésimo valor é usado, você deve:

1) destacar visualmente uma variável de matriz

 function updateActiveColumnsSetting(fieldsGroups) { var result = {}; for (var i = 0; i < fieldsGroups.length; i++) { var fields = fieldsGroups[i].fields; for (var j = 0; j < fields.length; j++) { if (!result[fieldsGroups[i].groupName]) { result[fieldsGroups[j].groupName] = {}; } result[fieldsGroups[i].groupName][fields[j].field] = createColumnAttributes(j, fields[j].isActive); } } return JSON.stringify(result); } 

2) analisar cada ocorrência para o uso do indexador desejado I, j, i-1, j-1, etc., tendo em vista os locais de uso dos indexadores e referências de percepção já identificadas.
Ao destacar o indexador em uma variável, reduzimos o número de locais perigosos e podemos usar facilmente o cérebro para perceber a variável, sem a necessidade de memorização.

Após o processamento:

 function updateActiveColumnsSetting(fieldsGroups) { var columnsGroups = {}; for (var i = 0; i < fieldsGroups.length; i++) { var fieldsGroup = fieldsGroups[i]; var groupName = fieldsGroup.groupName; var columnsGroup = columnsGroups[groupName]; if (!columnsGroup) { columnsGroup = columnsGroups[groupName] = {}; } var fields = fieldsGroup.fields; for (var j = 0; j < fields.length; j++) { var fieldInfo = fields[j]; columnsGroup[fieldInfo.field] = createColumnAttributes(j, field.isActive); } } return columnsGroups; } 

A seleção visual é muito mais simples, e os ambientes e editores de desenvolvimento modernos ajudam destacando substrings em diferentes partes do código.

 function updateActiveColumnsSetting(fieldsGroups) { var columnsGroups = {}; for (var i = 0; i < fieldsGroups.length; i++) { var fieldsGroup = fieldsGroups[i]; var groupName = fieldsGroup.groupName; var columnsGroup = columnsGroups[groupName]; if (!columnsGroup) { columnsGroup = columnsGroups[groupName] = {}; } var fields = fieldsGroup.fields; for (var j = 0; j < fields.length; j++) { var fieldInfo = fields[j]; columnsGroup[fieldInfo.field] = createColumnAttributes(j, field.isActive); } } return columnsGroups; } 

Regra 4. Agrupe os blocos de acordo com o significado.

Usamos o efeito psicológico da percepção - "O efeito da proximidade": figuras estreitamente espaçadas na percepção são combinadas. Para preparar o código para análise e generalização no processo de percepção e reduzir a quantidade de informações armazenadas na memória, você pode organizar uma linha uma ao lado da outra, unidas por significado ou por funcionalidade, dividindo-as por uma linha vazia.

Para:

 foreach(var abcFactInfo in abcFactInfos) { var currentFact = abcInfoManager.GetFact(abcFactInfo); var percentage = GetPercentage(summaryFact, currentFact); abcInfoManager.SetPercentage(abcFactInfo, percentage); accumPercentage += percentage; abcInfoManager.SetAccumulatedPercentage(abcFactInfo, accumPercentage); var category = GetAbcCategory(accumPercentage, categoryDictionary); abcInfoManager.SetCategory(abcFactInfo, category); } 

Depois:

 foreach (var abcFactInfo in abcFactInfos) { var currentFact = abcInfoManager.GetFact (abcFactInfo); var percentage = GetPercentage(summaryFact, currentFact); accumPercentage += percentage; var category = GetAbcCategory(accumPercentage, categoryDictionary); abcInfoManager.SetPercentage(abcFactInfo, percentage); abcInfoManager.SetAccumulatedPercentage(abcFactInfo, accumPercentage); abcInfoManager.SetCategory(abcFactInfo, category); } 

No exemplo superior, existem 7 blocos, nos 3 inferiores: obtendo valores, acumulando em um loop, definindo propriedades do gerenciador.

O recuo é bom para identificar lugares que merecem atenção. Então, as linhas

 accumPercentage += percentage; var category = GetAbcCategory(accumPercentage, categoryDictionary); 

além de depender dos cálculos anteriores, eles acumulam valores na variável acumulatedPercentage. Para enfatizar as diferenças, o código é recuado.

Um caso de uso específico para uma regra é declarar variáveis ​​locais o mais próximo possível do local de uso.

Regra 5. Seguindo o princípio da exclusividade de responsabilidade.

Este é o primeiro dos princípios do SOLID no OOP, mas, além das classes, pode ser aplicado a projetos, módulos, funções, blocos de código, equipes de projeto ou desenvolvedores individuais. Quando perguntado quem ou o que é responsável por essa área, fica imediatamente claro quem ou o quê. Uma conexão é sempre simples. Cada classe é generalizada para um conceito, frase, metáfora. Como resultado, menos para lembrar, o processo de percepção é mais fácil e mais eficiente.

Em conclusão, um exemplo abrangente:

 private PartnerState GetPartnerStateForUpdate( PartnerActivityInfo partner, Dictionary<int, PartnerState> currentStates, Dictionary<int, PartnerState> prevStates) { PartnerState updatingState; if (prevStates.ContainsKey(partner.id)) { if (currentStates.ContainsKey(partner.id)) { var prevState = prevStates[partner.id]; updatingState = currentStates[partner.id]; // 1 } else { // 2 } } else if (currentStates.ContainsKey(partner.id)) { updatingState = currentStates[partner.id]; } else { throw new Exception(string.Format("  {0}         {1}", partner.id, month)); } return updatingState; } 

Depois de substituir os indexadores ContainsKe, inverter a ramificação, selecionar o método e reduzir os níveis de aninhamento, descobriu-se:

 private PartnerState GetPartnerStateForUpdate( PartnerActivityInfo partner, Dictionary<int, PartnerState> currentStates, Dictionary<int, PartnerState> prevStates) { PartnerState currentState = null; PartnerState prevState = null; prevStates.TryGetValue(partner.id, out prevState); currentStates.TryGetValue(partner.id, out currentState); currentState = CombineStates(currentState, prevState); return currentState; } private PartnerState CombineStates( PartnerState currentState, PartnerState prevState) { if (currentState == null && prevState == null) { throw new Exception(string.Format( "  {0}         {1}" , partner.id, month)); } if (currentState == null) { // 1 } else if (prevState != null) { // } return currentState; } 

A primeira função é responsável por obter estados dos dicionários, a segunda por combiná-los em um novo.

As regras apresentadas são simples, mas combinadas, elas fornecem uma ferramenta poderosa para simplificar o código e reduzir a carga de memória durante o processo de desenvolvimento. Você nem sempre será capaz de segui-los, mas se perceber que não entende o código, é mais fácil começar com eles. Eles ajudaram repetidamente o autor nos casos mais difíceis.

Source: https://habr.com/ru/post/pt436160/


All Articles