gravatar

Cómo mejorar calidad de sus programas - Parte 3



Conceptos básicos (continuación)


No se puede hablar de ventajas o desventajas de algo en forma absoluta, sino siempre en comparación con otra cosa. En este caso, las ventajas de la programación modular y estructurada se realizan en comparación con la programación monolítica y basada en saltos.

Por las mismas razones las desventajas de la programación modular y estructurada se realizan en comparación con la programación orientada a objetos. Para conocer las características de la programación orientada a objetos que no están presentes en el estilo anterior de programación modular y estructurada lea Cómo entender la POO desde la perspectiva del programador estructurado que en síntesis son: un nivel mucho más importante de encapsulamiento, agregación, herencia y polimorfismo.

Las listas que se presentan a continuación tienen el objetivo de brindarle un panorama claro al respecto, pero no son necesariamente exhaustivas.

Ventajas de la programación modular y estructurada


  • Hace posible la lectura secuencial de los programas y con ello facilita su comprensión.
  • Permite reducir significativamente los errores producto de efectos colaterales inadvertidos.
  • Debido a que el código es más fácil de comprender (si se lo compara con código que no usa estructuras de control del flujo de ejecución), los errores también suelen ser más fáciles de encontrar y corregir.
  • Lo anterior hace que también sea más sencillo extender los programas (mantenimiento).
  • Consecuencia directa de lo anterior es que el mantenimiento de los programas suele tener un costo más reducido.

Desventajas de la programación estructurada


  • No permite modelar directamente los conceptos del dominio del problema, por lo que el nivel de abstracción que se puede lograr es menor que en la programación orientada a objetos: mientras que el problema se expresa en la terminología de los usuarios (los conceptos presentes en el dominio del problema), los programadores expresan el programa en su propio léxico (una mezcla de los conceptos menos abstractos del dominio del problema con un vocabulario que deriva del modo en que las computadoras funcionan, del lenguaje de programación y de otros vocablos de la jerga particular de los programadores). La necesaria traducción desde el dominio del problema al dominio de la solución requiere un mayor esfuerzo de compresión. Esto tiene un corolario respecto de la implementación que explica el siguiente ítem.
  • La separación entre datos (variables) y rutinas (procedimientos y funciones) conduce a la necesidad de utilizar variables globales e implica que las capacidades de encapsulamiento que el paradigma propone sean limitadas: esto conlleva el riesgo constante de producir efectos colaterales (modificaciones del estado del programa) no deseados que suelen introducir errores difíciles de encontrar.
  • Una consecuencia de lo anterior es que la introducción de cambios suele ser muy problemática ya que los efectos secundarios (o colaterales) aparecen con frecuencia durante el mantenimiento de los programas. Es muy habitual que al añadir una característica a un programa o incluso al corregir un error se introduzcan inadvertidamente efectos secundarios que provocan nuevos errores.
  • Inflexibilidad: es más difícil introducir cambios porque ello requiere mayores modificaciones en el código que ya está funcionando.

Legibilidad


La legibilidad es la facilidad para leer comprensivamente el código fuente de un programa. Como es de esperar, es una característica fundamental para el trabajo en equipo, para que posteriormente sea posible introducir cambios en el código con facilidad y para que resulte un poco más sencillo encontrar y corregir errores.

La legibilidad depende del paradigma de programación (por ejemplo, se logra mayor legibilidad en un programa orientado a objetos), de la correcta modularización, del uso adecuado de los recursos del lenguaje de programación, de la adecuada asignación de nombres a clases, rutinas, variables y constantes, y de un buen estilo de programación (uso de espacios en blanco, indentación y otras convenciones de programación).

Mantenibilidad


El término mantenibilidad se refiere a la facilidad o dificultad que se debe enfrentar para corregir fallos o introducir nuevas características a una aplicación luego de su puesta en producción.

Actualmente la mantenibilidad o facilidad de mantenimiento es una de las características más importantes que se busca al desarrollar una aplicación y muchas veces se le asigna un valor mucho mayor que a otras como el rendimiento. Sólo al desarrollar programas de base (sistemas operativos, controladores, etc.) o de tiempo real, el rendimiento suele colocarse por encima o al mismo nivel de prioridad que la mantenibilidad.

Lograr que un programa sea fácilmente mantenible implica que debe ser desarrollado cuidando que cumpla con ciertos criterios de diseño que se basan en muchos de los conceptos expuestos en este cómo.

Encapsulamiento


El encapsulamiento u ocultación es un concepto esencial en el diseño modular de programas que se aplica tanto cuando se sigue el paradigma de programación modular y estructurada como cuando se sigue el paradigma de programación orientada a objetos.

Al nivel más elemental, un grupo de sentencias se encapsula en una función, un procedimiento o un método de modo que su implementación quede oculta, lo que implica la existencia de una interfaz de comunicación con el exterior conformada por su firma.

Por ejemplo, una función toma un conjunto de parámetros (que junto con su nombre conforman su interfaz con el resto del programa). A través de la interfaz se comunica el conjunto de datos de entrada con los que la función opera. Con esos datos la función realiza un cálculo y devuelve un resultado, para lo que generalmente necesita de variables auxiliares que se declaran locales y por tanto están ocultas hacia el exterior al igual que las sentencias que la componen.

El encapsulamiento ayuda a evitar efectos secundarios y a lograr otra propiedad deseable como es la disminución del acoplamiento entre rutinas u objetos.

Como se mencionó antes la programación orientada a objetos permite niveles de encapsulamiento mucho mayores.

Efectos colaterales


Un efecto colateral o secundario es la consecuencia de que una rutina o expresión modifique el estado del programa esto es, por lo general, que asigne un nuevo valor a alguna variable. En el paradigma de programación imperativa (modular y estructurado u orientado a objetos), el cambio de estado resulta necesario para comunicar al programa con el mundo exterior (con otro programa, con un dispositivo de hardware o con un usuario).

Los efectos colaterales no deseados se producen generalmente debido al abuso en el uso de variables globales. Cuando se escriben rutinas de código que modifican variables globales, se contradicen los postulados básicos de la programación modular que establecen que los módulos (o subprogramas, es decir procedimientos y funciones) deben comunicarse con el resto del programa a través de sus parámetros (entrada) y de la devolución de valores (salida).

Cuando se utilizan variables globales en el cuerpo de las funciones, puede ocurrir que el comportamiento de una rutina A se vea alterada (inesperadamente) por una modificación a una variable global realizada previamente por una rutina B. También es posible la situación recíproca (B modifica una variable global y el comportamiento de A se ve alterado en una forma no deseada). El problema aumenta cuanto más grande es el programa que se desarrolla y cuanto más deficiente sea su diseño.

En la programación modular y estructurada los efectos secundarios inesperados fueron moneda corriente. Por el contrario, la programación orientada a objetos permite manejar los cambios de estado de una forma mucho más sencilla y segura (encapsulación); ello ayuda a minimizar la posibilidad de que se presenten efectos secundarios no deseados, aunque no permite eliminar esta posibilidad, ya que se depende de que el programador realice un buen diseño.

En Gambas no existen las variables globales. Obviamente esto significa que de un plumazo se elimina la posibilidad de que se cometa una gran cantidad errores.

Pero los efectos colaterales no deseados no se producen sólo por el uso de las variables globales. Cualquier procedimiento que reciba parámetros por referencia puede modificar los valores de esos parámetros y así producir efectos colaterales.

Aunque la posibilidad de producir efectos secundarios inesperados de este modo es menor que al usar variables globales, Gambas le brinda al programador una ayuda extra que consiste en asumir que los argumentos se pasan por valor a cualquier rutina (se le envía una copia de los originales): así dicha rutina no tiene forma de producir efectos colaterales.

En Gambas esto es válido siempre que se trate de tipos de datos nativos. En cambio, tratándose de objetos el paso por valor aún permite que una rutina utilice el objeto para introducir cambios de estado. En efecto, aunque el paso de los parámetros reales se realice por valor, tratándose de una referencia a un objeto, lo que se copia es la referencia (no se crea una copia del objeto), es decir, que se sigue apuntando al mismo objeto y por ello cualquier cambio de estado que permita el objeto es factible que se realice en el interior de la rutina que recibió la referencia como argumento.

Algunos aspectos que debería tener en cuenta:

  • La versión 3 de Gambas permite que cualquier rutina pueda provocar efectos colaterales a través de sus parámetros, pero ello se debe indicar explícitamente mediante la palabra clave BYREF tanto en la declaración de los parámetros formales de la rutina, como en sus llamadas (este doble requisito hace que sea imposible que el programador utilice el paso por referencia inadvertidamente). El pasaje de parámetros por referencia fue habitual en el estilo de programación modular y estructurado e incluso a veces recomendado como una forma de limitar el uso de variables globales. No obstante, se desaconseja su uso y Gambas incluye esta posibilidad sólo para facilitar la traducción del código de programas escritos en VB.
  • Una mala modularización convierte a cualquier variable pública (declarada mediante la palabra clave PUBLIC) en una variable global en su propio ámbito. Aunque esto no es lo mismo que una variable global, puede producir exactamente los mismos problemas a una escala menor.
  • Como se indicó antes, el paso de parámetros por valor no impide los cambios de estado cuando los argumentos son referencias a objetos.

Cohesión


La cohesión es el grado de relación que guardan las sentencias que componen una rutina, es decir un procedimiento o una función. Pero el concepto también se aplica a las rutinas en agrupadas en unidades o módulos y, si se usa el paradigma de POO, a las clases.

El objetivo es que las sentencias dentro de las rutinas sean tan cohesivas como sea posible. Básicamente esto significa que una rutina debe cumplir una única tarea a un determinado nivel conceptual, es decir, que las sentencias y expresiones que contenga deben mantener un mismo nivel de abstracción.

Una receta casi inexpugnable para modularizar correctamente es utilizar nombres expresivos, cumplir fielmente con el principio de responsabilidad única y si la responsabilidad implica la realización de diversas sub-tareas, delegarlas en otras rutinas que tendrán un nivel de abstracción más bajo.

Un procedimiento muy sencillo para controlar si un método (función o procedimiento) es cohesivo, es verificar que su nombre sea descriptivo de lo que hace el código que contiene. Si al realizar esta verificación encontramos que para mantener la coherencia entre el nombre de la rutina y su código debemos ampliar el nombre (algo como HaceAHaceBHaceC...), entonces la rutina no es cohesiva y debemos refactorizarla.

Otro método heurístico y complementario para verificar la cohesión consiste verificar la aridad de la rutina. Idealmente una rutina debería tener uno o dos parámetros, en casos no tan comunes podría llegar a tener tres, una rutina con cuatro parámetros constituiría un caso excepcional. Si supera los cuatro parámetros es altamente probable que la rutina no sea cohesiva.

Esto también se aplica módulos o unidades que agrupan rutinas que deben guardar relación entre sí.


Acoplamiento


El acoplamiento es un indicador del grado de dependencia que existe entre diferentes unidades de software (rutinas, módulos, clases, componentes, etc). Cuanto más intrincada es la relación de dependencia es más difícil comprender y mantener el programa.

Por el contrario, cuanto más independientes son esas unidades de software, además de ser más fácilmente comprensibles y por lo tanto más fácilmente mantenibles, también es más fácil que se las pueda volver a usar dentro del mismo u otros proyectos de desarrollo (re-utilización).

Por ello, al escribir las rutinas que conforman un programa, un objetivo frecuente es que las rutinas queden tan desacopladas como sea posible.





Continuar leyendo...