5 reglas de código fáciles de leer

Te preguntas: "¿Cómo escribir de forma limpia y comprensible?" ¿Cómo averiguar rápidamente el código de otra persona? "

¡Recuerde las siguientes reglas y aplíquelas!

El artículo no trata las reglas básicas para nombrar variables y funciones, sangría sintáctica y un tema de refactorización a gran escala. Consideramos 5 reglas simples para simplificar el código y reducir la carga en el cerebro durante el proceso de desarrollo.

Considere el proceso de percibir datos para correlacionar las reglas descritas con el proceso de percepción y determinar los criterios para un código simple.

El proceso simplificado de percepción consta de los siguientes pasos:

  1. Los datos recibidos a través de los receptores son consistentes con la experiencia previa.
  2. Si no hay correlación, esto es ruido. El ruido se olvida rápidamente. Si hay algo con lo que relacionarse, se reconoce el hecho.
  3. Si el hecho es importante, recordamos, generalizamos o actuamos, por ejemplo, decimos o escribimos el código.
  4. Para reducir la cantidad de información memorizada y analizada, se utiliza la generalización.
  5. Después de resumir, la información se correlaciona y analiza nuevamente (paso 1).


Los científicos dividen la memoria en corto y largo plazo. La memoria a corto plazo es pequeña en volumen, pero la información se extrae y almacena instantáneamente. La memoria a corto plazo es la memoria caché del cerebro. Puede almacenar 7 + -2 palabras, números, objetos. La memoria a largo plazo es más grande en volumen, pero requiere más energía (esfuerzo) para guardar y recuperar información que la memoria a corto plazo.

Conclusiones:

  • cuantos menos elementos, menos energía se gasta,
  • se requiere reducir el número de elementos percibidos a 9,
  • use la memoria a largo plazo lo menos posible: generalice u olvide.

Procedemos a la descripción de las reglas.

Regla 1. Usamos la declaración en las condiciones, nos deshacemos del "no".

Eliminar el operador "no" reduce el número de elementos que se analizan. Además, en muchos lenguajes de programación, se utiliza un "!" Para el operador de negación. Este carácter es fácil de omitir al leer el código.

Compara:

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

Después:

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

Regla 2. Reducción del nivel de anidación.

Durante el análisis de código, cada nivel de anidamiento debe mantenerse en la memoria. Reduzca la cantidad de niveles: reduzca el costo del combustible.

Considere formas de reducir el número de niveles de anidación.

1) Control de retorno. Cortamos algunos de los casos y nos enfocamos en los restantes.

 if (conributor != null) { // } 

Convertir a

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

o

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

o

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

Se da un ejemplo extendido en la regla 5. Observe el lanzamiento de una excepción.

2) Aislamiento del método. El nombre de la función es el resultado de la generalización.

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

en

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

3) Condiciones combinadas.

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

en

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

4) Eliminar variables y dividirlas en bloques semánticos. Como resultado, los bloques se pueden cambiar de forma independiente y, lo que es más importante, leer y comprender dicho código es mucho más fácil. Un bloque: una funcionalidad. Un caso común es una búsqueda con procesamiento. Es necesario dividir el código en bloques semánticos separados: búsqueda de elementos, procesamiento.

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

en

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

Regla 3. Nos deshacemos de los indexadores y las llamadas a través de propiedades.

Indexador: la operación de acceder a un elemento de una matriz en el índice arr [index].

En el proceso de percibir el código, el cerebro opera con los símbolos [] como delimitadores, y el indexador como una expresión.

 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 indexador es un lugar de errores frecuentes. En virtud de los nombres de índice cortos, es muy fácil mezclar I, j o ​​k.

En el ejemplo anterior, en el resultado de la línea [fieldsGroups [j] .groupName] = {}; error cometido:
j se usa en lugar de i.

Para encontrar dónde se usa exactamente el i-ésimo valor, debe:

1) resaltar visualmente una variable 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) analizar cada ocurrencia para el uso del indexador deseado I, j, i-1, j-1, etc., teniendo en cuenta los lugares de uso de los indexadores y las referencias ya identificadas en la percepción.
Al resaltar el indexador en una variable, reducimos el número de lugares peligrosos y podemos usar fácilmente el cerebro para percibir la variable, sin la necesidad de memorización.

Después de procesar:

 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 selección visual es mucho más simple, y los entornos de desarrollo y editores modernos ayudan al resaltar las subcadenas en diferentes partes del 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; } 

Regla 4. Agrupe los bloques de acuerdo con el significado.

Usamos el efecto psicológico de la percepción: "El efecto de la proximidad": se combinan figuras espaciadas en la percepción. Para preparar el código para el análisis y la generalización en el proceso de percepción, y para reducir la cantidad de información almacenada en la memoria, puede organizar una fila una al lado de la otra, unidas por el significado o cerca de la funcionalidad, dividiéndolas por una línea vacía.

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

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

En el ejemplo superior hay 7 bloques, en los 3 inferiores: obtener valores, acumular en un bucle, establecer propiedades del administrador.

La sangría es buena para identificar lugares a los que vale la pena prestarles atención. Entonces, las líneas

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

Además de depender de cálculos anteriores, acumulan valores en la variable acumulada Porcentaje. Para enfatizar las diferencias, el código está sangrado.

Un caso de uso particular para una regla es declarar las variables locales lo más cerca posible del lugar de uso.

Regla 5. Siguiendo el principio de unicidad de responsabilidad.

Este es el primero de los principios SÓLIDOS en OOP, pero además de las clases se puede aplicar a proyectos, módulos, funciones, bloques de código, equipos de proyecto o desarrolladores individuales. Cuando se le pregunta quién o qué es responsable de esta área, inmediatamente queda claro quién o qué. Una conexión siempre es simple. Cada clase se generaliza a un concepto, frase, metáfora. Como resultado, menos para recordar, el proceso de percepción es más fácil y más eficiente.

En conclusión, un ejemplo completo:

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

Después de reemplazar los indexadores ContainsKe, invertir la ramificación, asignar el método y reducir los niveles de anidación, obtuvimos

 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 primera función es responsable de obtener estados de los diccionarios, la segunda de combinarlos en uno nuevo.

Las reglas presentadas son simples, pero en combinación proporcionan una herramienta poderosa para simplificar el código y reducir la carga de memoria durante el proceso de desarrollo. No siempre podrá seguirlos, pero si se da cuenta de que no comprende el código, es más fácil comenzar con ellos. Han ayudado repetidamente al autor en los casos más difíciles.

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


All Articles