5 règles de code faciles à lire

Vous vous demandez: "Comment écrire proprement, de façon compréhensible?" Comment comprendre rapidement le code de quelqu'un d'autre? "

N'oubliez pas les règles ci-dessous et appliquez-les!

L'article ne traite pas des règles de base pour nommer les variables et les fonctions, l'indentation syntaxique et une rubrique de refactorisation à grande échelle. Nous considérons 5 règles simples pour simplifier le code et réduire la charge sur le cerveau pendant le processus de développement.

Considérez le processus de perception des données pour corréler les règles décrites avec le processus de perception et déterminer les critères d'un code simple.

Le processus simplifié de perception comprend les étapes suivantes:

  1. Les données reçues par les récepteurs sont cohérentes avec l'expérience antérieure.
  2. S'il n'y a pas de corrélation, c'est du bruit. Le bruit est vite oublié. S'il y a quelque chose auquel se rapporter, le fait est reconnu.
  3. Si le fait est important - nous nous souvenons, ou généralisons, ou agissons, par exemple, nous disons ou tapons le code.
  4. Pour réduire la quantité d'informations mémorisées et analysées, la généralisation est utilisée.
  5. Après récapitulation, les informations sont à nouveau corrélées et analysées (étape 1).


Les scientifiques divisent la mémoire en court terme et long terme. La mémoire à court terme est de petit volume, mais les informations sont extraites et stockées instantanément. La mémoire à court terme est la cache du cerveau. Il peut stocker 7 + -2 mots, nombres, objets. La mémoire à long terme est plus volumineuse, mais elle nécessite plus d'énergie (effort) pour enregistrer et récupérer des informations que la mémoire à court terme.

Conclusions:

  • moins il y a d'éléments, moins d'énergie est dépensée,
  • il faut réduire le nombre d'éléments perçus à 9,
  • utiliser le moins possible la mémoire à long terme: généraliser ou oublier.

Nous passons à la description des règles.

Règle 1. Nous utilisons l'énoncé dans les conditions, débarrassons-nous du «non».

La suppression de l'opérateur «non» réduit le nombre d'éléments analysés. De plus, dans de nombreux langages de programmation, un «!» Est utilisé pour l'opérateur de négation. Ce caractère est facile à ignorer lors de la lecture du code.

Comparez:

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

Après:

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

Règle 2. Réduction du niveau d'imbrication.

Pendant l'analyse de code, chaque niveau d'imbrication doit être conservé en mémoire. Réduisez le nombre de niveaux - réduisez le coût du carburant.

Envisagez des moyens de réduire le nombre de niveaux d'imbrication.

1) Contrôle de retour. Nous avons supprimé certains des cas et nous nous concentrons sur les autres.

 if (conributor != null) { // } 

Convertir en

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

ou

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

ou

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

Un exemple étendu est donné dans la règle 5. Notez le lancement d'une exception.

2) Isolement de la méthode. Le nom de la fonction est le résultat de la généralisation.

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

dans

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

3) Combiner les conditions.

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

dans

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

4) Suppression de variables et division en blocs sémantiques. Par conséquent, les blocs peuvent être modifiés indépendamment et, surtout, la lecture et la compréhension de ce code sont beaucoup plus faciles. Un bloc - une fonctionnalité. Un cas courant est une recherche avec traitement. Il est nécessaire de diviser le code en blocs sémantiques séparés: recherche d'éléments, traitement.

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

dans

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

Règle 3. Nous nous débarrassons des indexeurs et des appels via les propriétés.

Indexeur - l'opération d'accès à un élément d'un tableau à l'index arr [index].

Dans le processus de perception du code, le cerveau fonctionne avec les symboles [] comme délimiteurs, et l'indexeur comme expression.

 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); } 

Un indexeur est un lieu d'erreurs fréquentes. Grâce à des noms d'index courts, il est très facile de mélanger I, j ou k.

Dans l'exemple ci-dessus, dans la ligne résultat [fieldsGroups [j] .groupName] = {}; erreur commise:
j est utilisé à la place de i.

Afin de trouver où exactement la i-ème valeur est utilisée, vous devez:

1) surlignez visuellement une variable de tableau

 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) d'analyser chaque occurrence pour l'utilisation de l'indexeur souhaité I, j, i-1, j-1, etc., en tenant compte des lieux d'utilisation des indexeurs et des références déjà identifiées dans la perception.
En mettant en évidence l'indexeur dans une variable, nous réduisons le nombre d'endroits dangereux et nous pouvons facilement utiliser le cerveau pour percevoir la variable, sans avoir besoin de la mémoriser.

Après le traitement:

 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; } 

La sélection visuelle est beaucoup plus simple, et les environnements de développement et les éditeurs modernes aident en mettant en évidence les sous-chaînes dans différentes parties du code.

 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; } 

Règle 4. Regroupez les blocs selon leur signification.

Nous utilisons l'effet psychologique de la perception - «L'effet de la proximité»: des figures rapprochées de la perception sont combinées. Pour préparer le code à l'analyse et à la généralisation dans le processus de perception, et pour réduire la quantité d'informations stockées en mémoire, vous pouvez organiser une rangée les unes à côté des autres, unies par la signification ou proches dans la fonctionnalité, en les divisant par une ligne vide.

À:

 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); } 

Après:

 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); } 

Dans l'exemple supérieur, il y a 7 blocs, dans le 3 inférieur: obtenir des valeurs, s'accumuler dans une boucle, définir les propriétés du gestionnaire.

L'indentation est bonne pour identifier les endroits qui méritent une attention particulière. Ainsi, les lignes

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

en plus de dépendre des calculs précédents, ils accumulent des valeurs dans la variable accumulatedPercentage. Pour souligner les différences, le code est en retrait.

Un cas d'utilisation particulier pour une règle est de déclarer des variables locales aussi proches que possible du lieu d'utilisation.

Règle 5. Respect du principe de l'unicité de responsabilité.

Il s'agit du premier principe SOLID de la POO, mais en plus des classes, il peut être appliqué à des projets, modules, fonctions, blocs de code, équipes de projet ou développeurs individuels. Lorsqu'on lui demande qui ou quoi est responsable de ce domaine, il est immédiatement clair qui ou quoi. Une connexion est toujours simple. Chaque classe est généralisée à un concept, une phrase, une métaphore. En conséquence, moins à retenir, le processus de perception est plus facile et plus efficace.

En conclusion, un exemple complet:

 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; } 

Après avoir remplacé les indexeurs ContainsKe, inversé la ramification, sélectionné la méthode et réduit les niveaux d'imbrication, il s'est avéré:

 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; } 

La première fonction est chargée d'obtenir des états à partir des dictionnaires, la seconde de les combiner en un nouveau.

Les règles présentées sont simples, mais en combinaison, elles fournissent un outil puissant pour simplifier le code et réduire la charge mémoire pendant le processus de développement. Vous ne pourrez pas toujours les suivre, mais si vous réalisez que vous ne comprenez pas le code, il est plus facile de commencer avec eux. Ils ont aidé à plusieurs reprises l'auteur dans les cas les plus difficiles.

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


All Articles