¿Cómo puedo definir y medir la simplicidad en el código?

13

Hay muchas respuestas en mi pregunta anterior sobre la simplicidad relacionada con la legibilidad que me ayudaron a ver que mi definición y comprensión de la simplicidad en el código era, posiblemente, incorrecta.

¿Cómo puedo definir la simplicidad en el código? ¿Qué medidas y métricas de software están disponibles para medir la simplicidad del código?

Ricardo
fuente
2
@ MarkTrapp Hay otras formas de discutir la simplicidad del código sin temas de ingeniería de software empírica, temas con los que estoy mucho menos familiarizado. Por ejemplo, discutir la simplicidad en términos de la capacidad de escribir pruebas automatizadas. Mis habilidades y conocimientos me permiten responder a esta pregunta desde la perspectiva de un ingeniero de software empírico, mientras que otros pueden responder desde perspectivas alternativas. Agregar esa declaración a la pregunta limita significativamente el número de respuestas útiles, por lo que (IMO) está demasiado localizado. Si desea agregarlo, puede hacerlo, pero esta es una buena pregunta.
Thomas Owens
2
@ThomasOwens Las preguntas reales tienen respuestas , no ideas u opiniones. Reducir el alcance para que todos interpreten cómo responder la pregunta de la misma manera es exactamente de lo que trata Stack Exchange. Puede haber más de un enfoque para resolver el problema, pero solo hay un problema sin ambigüedades.
En su estado actual, hay muy pocas respuestas a esta pregunta (mi respuesta aborda el punto de vista empírico de ingeniería de software, con las métricas comunes, probablemente hay otras). No tiene sentido excluir respuestas que ofrecen alternativas válidas desde otras perspectivas, que es lo que hace la redacción de esta pregunta. No estoy totalmente de acuerdo con estas ediciones y la pregunta debería volver a su forma original.
Thomas Owens
@MarkTrapp El problema no es ambiguo: ¿cómo determino la simplicidad del código? Hay varias buenas respuestas. El mío es utilizar técnicas empíricas de ingeniería de software para medir la complejidad. Otro podría ser escribir pruebas automatizadas y si es difícil escribir buenas pruebas, el código es complejo, una respuesta perfectamente válida. Puede haber otros que no conozco. Si necesita medir la complejidad / simplicidad de una base de código, la pregunta debe formularse de manera que permita que se presenten todas las alternativas para que el autor de la pregunta pueda elegir la mejor solución para su caso particular.
Thomas Owens

Respuestas:

16

Las métricas más comunes para medir la complejidad (o simplicidad, si considera que la simplicidad es lo opuesto a la complejidad) son la Complejidad Ciclomática de McCabe y las Métricas de Complejidad de Halstead .

La complejidad ciclomática mide el número de rutas distintas a través de una unidad dada, generalmente un método o función, aunque también se puede calcular en una clase. A medida que aumenta el número de rutas, se hace más difícil recordar el flujo de datos a través de un módulo dado, que está relacionado con el concepto de memoria de trabajo . La alta complejidad ciclomática tiende a indicar dificultad en la capacidad de probar un módulo; se requieren más casos de prueba para cubrir las diversas rutas a través del sistema. También se han realizado estudios que han relacionado la alta complejidad ciclomática con las altas tasas de defectos. Típicamente, una complejidad ciclomática de 10 indica que una unidad debe ser revisada y posiblemente refactorizada.

Las medidas de complejidad de Halstead utilizan las entradas de operadores y operandos totales y distintos para calcular el volumen, la dificultad y el esfuerzo de un fragmento de código. La dificultad, que es el (número de operadores únicos / 2) * (número total de operandos / número de operandos únicos), está ligada a la capacidad de leer y comprender el código para tareas tales como aprender el sistema o realizar una revisión del código. Nuevamente, puede contar esto en un nivel de sistema, un nivel de clase o un nivel de método / función. Hay algunas publicaciones sobre cómo calcular estas medidas aquí y aquí .

Simplemente contar líneas de código también puede darle una idea de la complejidad. Más líneas de código significa que hay más para leer y comprender en un módulo. Dudaría en usar esto como una medida independiente. En cambio, lo usaría con otras mediciones, como el número de defectos en un módulo dado para obtener la densidad de defectos. Una alta densidad de defectos podría indicar problemas al escribir pruebas y realizar revisiones de código, que pueden o no ser causadas por código complejo.

Fan-in y fan-out son otras dos métricas relacionadas con el flujo de datos. Como se define aquí , fan in es la suma de los procedimientos llamados, los parámetros leídos y las variables globales leídas y desplegadas es la suma de los procedimientos que llaman a un procedimiento dado, los parámetros escritos (expuestos a usuarios externos, pasados ​​por referencia), y variables globales escritas en. Nuevamente, un alto nivel de entrada y salida puede ser indicativo de un módulo que puede ser difícil de entender.

En paradigmas específicos, puede haber otras medidas o métricas que también sean útiles. Por ejemplo, en el mundo orientado a objetos, la monitorización del acoplamiento (deseo bajo), la cohesión (deseo alto) y la profundidad de la herencia (deseo bajo) se pueden usar para evaluar qué tan simple o complicado es un sistema.

Por supuesto, es importante darse cuenta de que muchas medidas y métricas son simplemente indicadores. Debe usar su criterio para determinar si es necesario refactorizar para aumentar la simplicidad o si no vale la pena hacerlo. Puede realizar las mediciones, calcular las métricas y conocer su código, pero no desea diseñar su sistema por los números. En definitiva, haz lo que tenga sentido.

Thomas Owens
fuente
55
Sé que lo mencionó, pero es importante destacar que la complejidad ciclomática en realidad solo es útil en el nivel de función / método y se vuelve significativamente más subjetiva / inútil en los niveles superiores.
Ryathal
El problema es que si bien estas medidas son buenas una guía general. Hay varios casos en los que los programas "malos" obtienen buenos resultados, por ejemplo, tener una docena de funciones, donde una sola función con dos parámetros más sería suficiente, y, a la inversa, muchos programas "buenos" que son soluciones bien escritas para un problema complejo pueden calificar mal.
James Anderson
@ James, lo señalé explícitamente. Cualquier medida o métrica debe tomarse en contexto como un indicador de que algo debe considerarse. Se necesita el juicio de un ingeniero para determinar si se necesita una acción correctiva y cuál es esa acción. Sin embargo, a menos que esté recopilando datos de forma activa, no existe una forma empírica de conocer los posibles problemas.
Thomas Owens
7

En lugar de mirar un modo formal de definir la simplicidad, preferiría definir la simplicidad como un atributo de calidad de escritura de código.

No estoy poniendo algo de simplicidad, pero ¿cuándo llamas a algo simple o no?

1. Código transversal:
¿Qué tan fácil es navegar por el código? ¿Es fácil detectar dónde se escriben las funciones de la API? ¿Es fácil entender los flujos de llamadas, por ejemplo, qué métodos están llamando a otros (y por qué)? ¿Hay buenas máquinas de estado implementadas o algoritmos claramente identificados?

Cuando el recorrido del código es fácil, el código es simple de seguir.

2.
Nombramiento Mientras que otros estándares de codificación ayudan a que el código se vea más limpio, lo más importante es nombrar clases / instancias de objetos / Variables / métodos. El uso de nombres claros e inequívocos claramente tiene un gran impacto en la simplicidad del código. Cuando es difícil identificar un nombre simple, es una señal de que tal vez quiera repensar la idea de esa variable / método.

3. Interpretación y referencias
¿Tiene cada uno de sus métodos un papel claro que desempeñar? ¿Cada variable / atributo es fácil de determinar el papel que están jugando? Cuando un código hace algo que implica suposiciones o afecta a un conjunto de variables no relacionadas, puede convertirse en una pesadilla de mantenimiento.

4. Dependencia o acoplamiento
Esto es difícil de juzgar con solo mirar el código, pero se vuelve muy evidente si alguien intenta corregir sus errores. Cuando otras cosas cambian en algún otro objeto, ¿cambia la operación aquí? ¿Son obvios esos cambios? ¿Necesita cambiar la API con tanta frecuencia para acomodar cosas? Esto sugiere que las relaciones entre módulos no son simples

5. Entradas de usuario o aplicaciones
Finalmente, ¿qué tan simples son las entradas de usuario o aplicaciones aceptadas en la API / UI? Cuando múltiples usuarios / aplicaciones posibles (para diferentes propósitos) necesitan darle, ¿son obvios? ¿Hay estados / detalles que no están relacionados con la abstracción superior pero que aún van y vienen de la interfaz?

Una pregunta simple que generalmente haría es la siguiente: si en lugar de un programa, si hubiera pedido que un humano realizara la misma función, ¿habría llenado esta información en un formulario en papel ? Si no, no soy lo suficientemente simple aquí.

No diré que esta lista es exhaustiva, pero supongo que el criterio es cuán fácil o difícil es usar y modificar el software. Eso es simple

Dipan Mehta
fuente
1
Pidió medidas y métricas. Estos son subjetivos, por lo que no creo que sean muy precisos.
psr
@psr Estoy de acuerdo contigo. También fue muy evidente por la respuesta de la respuesta de Thomas. Sin embargo, mencionó la simplicidad relacionada con la legibilidad . La Respuesta de Thomas trata de la complejidad ciclomática: esto le dice cuán complejo es probar el código y no cuán complejo es el código en términos de legibilidad y puede extender la capacidad de mantenimiento . Estos son dos conceptos muy diferentes. Es precisamente por eso que escribí esta respuesta para poner la clara contradicción. Desafortunadamente, que yo sepa, no hay métricas que se refieran a la simplicidad del código en términos de legibilidad.
Dipan Mehta
"use nombres que son imposibles de entender" - En mi humilde opinión, esto apunta demasiado alto, a un objetivo irrealista e imposible. Prefiero no tratar de ser tan definido y simplemente decir "usar nombres claros e inequívocos".
Péter Török
@ PéterTörök Estoy de acuerdo. Creo que, en muchas organizaciones, persisten reglas muy claramente definidas de convenciones de nombres y aún persiste cierta confusión sobre la intención de la variable particular. Por lo tanto, se hizo hincapié en decir que la claridad del propósito equivale a la simplicidad en lugar de una regla formal. Puede ser que me excedí en la forma en que lo describí. Gracias.
Dipan Mehta
@Dipan La complejidad ciclomática está relacionada con la legibilidad del código, a través de la memoria de trabajo. El código con alta complejidad ciclomática (o incluso con una gran profundidad de bloque) es difícil de mantener en la memoria de trabajo, por lo tanto, es más difícil de leer directamente.
Thomas Owens
0

No conozco ninguna buena métrica existente para la simplicidad del código (no significa que no existan, solo que no las conozco). Podría proponer algunos, tal vez algunos ayuden:

  • Simplicidad de las características del lenguaje utilizadas: si el lenguaje tiene características que podrían considerarse "avanzadas" y "simples", podría contar el número de ocurrencias de las características avanzadas. La forma de definir "avanzado" podría ser un poco más subjetiva. Supongo que algunos podrían decir que esto también es como medir la "inteligencia" de un programa. Un ejemplo común: algunos podrían decir que el ?:operador debería ser una función "avanzada", otros podrían estar en desacuerdo. No sé lo fácil que sería escribir una herramienta que pueda probar esto.

  • Simplicidad de construcciones dentro del programa: puede medir el número de parámetros que aceptará una función. Si usted tiene> n % de todas las funciones con> m parámetros, se puede optar por contar como no simple, dependiendo de cómo se defina n y m (tal vez n = 3 ym = 6?). Creo que hay algunas herramientas de análisis estático que pueden medir esto; creo que JTest simplemente midió funciones con parámetros > m .

  • Podría intentar contar el número de bucles anidados o estructuras de control. Esto creo que en realidad no es una mala métrica y creo que hay un nombre para ello (no puedo recordar la parte superior de mi cabeza). Nuevamente, creo que hay herramientas (de nuevo, como JTest) que pueden medir esto, hasta cierto punto.

  • Podrías intentar medir la "refactorabilidad". Si su código contiene muchas piezas de código que podrían ser refactorizadas pero no lo son , tal vez eso podría no ser simple. También recuerdo desde el momento en que trabajé con JTest que también intentó medir esto, pero recuerdo que a menudo no estaba de acuerdo con eso en este caso, así que YMMV.

  • Podría intentar medir el número de capas entre diferentes partes de su sistema. Por ejemplo: ¿cuántos códigos diferentes tocarán los datos que provienen de un formulario web antes de que se almacenen en la base de datos? Esto podría ser complicado de medir correctamente ...

FrustratedWithFormsDesigner
fuente
2
Creo que # 3 se llama profundidad de bloque. También está relacionado con la Complejidad Ciclomática si hay estructuras de control decisionales involucradas.
Thomas Owens
¿Ninguna explicación negativa?
FrustratedWithFormsDesigner
No puedo estar de acuerdo con la "simplicidad de las características del lenguaje". Las funciones avanzadas están ahí para simplificar el código. El uso de características simples y básicas oscurecerá lo que el código realmente está haciendo, inevitablemente conduce a fugas en las capas de abstracción. Las características avanzadas del lenguaje permiten expresar niveles más altos de abstracción, dejando su código mucho más denso y legible. Cuantas más funciones avanzadas esté usando (sabiamente, por supuesto), mejor será la simplicidad. Simplemente compare un código en, digamos, Matlab (que de hecho es "avanzado") con un código Fortran similar hecho únicamente de características básicas.
SK-logic
Y tampoco estaré de acuerdo con una serie de capas métricas. Si puede hacer algo en una docena de pasos triviales y limpios o en una transformación retorcida, mejor hacerlo en varios pasos. Muchas capas simples y claramente separadas son mucho mejores (y más simples) que una sola capa retorcida.
SK-logic
@ SK-logic: supongo que debería haber llamado a eso "inteligencia", está más cerca de lo que quise decir. Solo diría que cosas como ?:son un problema cuando están anidadas a 5 de profundidad. En cuanto a las capas, las capas separadas limpiamente son mejores que una capa enrevesada. Pero 7 capas en su mayoría redundantes, cuando solo 2 o 3 eran necesarias, es algo malo.
FrustratedWithFormsDesigner