Enlace:

 

 

 

Técnicas para una Programación de Calidad.

Moisés Daniel Díaz Toledano. www.moisesdaniel.com

Introducción.

¿Qué es un error? Podríamos decir a  priori que un error es un fallo que se incluye en un programa estropeando el producto y afectando al cliente.

Podemos verlo también desde otra óptica. Cuando desarrollamos software intentamos proveer de cierta funcionalidad al usuario, si esta funcionalidad no se cumple, podemos decir que el software tiene errores.

Estos errores de los que estamos hablando, son errores cuantificables (detectables) por el usuario, lo que nos lleva al concepto de calidad externa del software.

Sin embargo, aún no hemos justificado el propósito de este artículo, ¿por qué hacer una programación de calidad? Porque la calidad externa del software guarda una relación directa con la calidad interna del mismo, es decir con su estructura y codificación.

Todos queremos construir software correcto, robusto, extensible y reusable. Debemos ser conscientes que el mejor camino para lograrlo, está precisamente en hacer una programación de calidad.

Existen además otras razones de mucha importancia para aplicar técnicas de buena codificación. Como desarrolladores no podemos olvidar que el código fuente se mantiene y reutiliza, con lo que para facilitar estas tareas debemos cuidar nuestra programación.

El objetivo del presente artículo es presentar de una forma simple, informal y no demasiada ordenada, algunas técnicas particulares, así como otras generales, para el ejercicio de una buena codificación. Quizás no se esté de acuerdo con algunos de estos consejos, pero al menos espero hacer reflexionar al lector sobre ellos, lo cual sin duda, traerá beneficios cuantificables a nuestros desarrollos.

 

 

1.- Nomenclatura

Los identificadores tanto de variables como de clases, funciones y procedimientos constituyen una buena parte del código. Su elección es muy importante ya que normalizan el código, y ayudan a entender el significado real de las acciones que realizan.

Es muy importante usar una nomenclatura bien definida en nuestra código, ya que aumenta enormemente la legibilidad y la semántica del mismo y hace más sencillo el trabajo en equipo y la supervisión por parte de otras personas . Tener en cuenta que los excesos son negativos  y que por tanto especificar reglas para absolutamente todo trae más problemas de los que soluciona.

La nomenclatura más ampliamente extendida es la ‘Notación Húngara’, con numerosísimos defensores y detractores, cada uno bien armado de argumentos. Nos guste o no la notación húngara, siempre podemos sacar provechosas enseñanzas de esta propuesta que presupone que es mucho más importante dar información en los nombres de los identificadores que poder leer el código en voz alta.

Una de las ideas centrales en esta notación es la del uso de prefijos que incluyan información sobre el tipo de los identificadores. Esta propuesta en concreto tiene ciertas desventajas en los IDE (Entornos Integrados de Desarrollo) actuales debido sobre todo a la existencia de herramientas que nos informan conforme vamos tecleando de los diferentes métodos, atributos, variables, etc disponibles en ese contexto particular.

Sin embargo, sí que podemos usar postfijos para especificar cierta información, en concreto información relacionada con el ámbito de validez de las variables, tema que hasta ahora ha sido poco tratado.

Una posible recomendación sería la de  usar como postfijos en los identificadores para indicar el ámbito de validez de ciertas variables los siguientes caracteres: 'C' para las variables de clase y 'G' para los objetos o variables de ámbito global.

 

modMain

Public oCnnG As ADODB.Connection ‘Esto es solo un ejemplo!! Solo usar objetos globales cuando es estrictamente ‘necesario

 

Public Sub main()

    ' algo de código

    Set oCnnG = New ADODB.Connection

    oCnnG.Open "DSN=XX;SERVER=XX", "XX", "XX"

    'más código

End Sub

 

Un pequeño ejemplo de clase desarrollada según las normas que se esbozan a lo largo de este artículo sería:

 

CFile

Option Explicit

'Comentarios de Clase.

 

Dim bConsistC As Boolean

Dim sFilePathC As String

 

Public Sub Init(psFilePath As String)

    Debug.Assert (Dir(psFilePath, vbArchive) <> "")

    sFilePathC = psFilePath

    bConsistC = True

End Sub

 

Public Function sGetFileText() As String

On Error GoTo errCatch

    Debug.Assert (bConsistC = True)

    Dim iFile As Integer

    Dim sTempText As String

      

    iFile = FreeFile

    Open sFilePathC For Input As #iFile

        sTempText = input$(LOF(iFile), #iFile)

    Close #iFile

   

    sGetFileText = sTempText

    Exit Function

errCatch:

    'Error code

End Function

 

2.- Uso profuso de Aserciones/Validaciones.

Uno de los pilares de una programación sólida es conseguir detectar rápidamente los errores durante el desarrollo para evitar que estos pasen al producto que entregamos al cliente.

El mecanismo principal para lograr este objetivo es el uso generalizado y profuso de validaciones en el código para detectar cualquier uso no válido de nuestros métodos, clases, funciones y procedimientos. A esto lo denomino ‘Blindaje de Clases y Bloques’.

Se recomienda como mínimo usarlas como precondiciones en nuestras funciones y procedimientos para validar: parámetros, estado del objeto, estados contextuales, etc. Un ejemplo general y abstracto podría ser el siguiente:

 

Class

Option Explicit

‘Comentarios sobre la clase

'Declaraciones a nivel de clase  (recuerda el último carácter con  'C')

 

Public Sub SomeProcedure ( parameters list )

On error errCatch

    'Validación del estado de la clase (variables de clase y objetos relacionados con esta clase!!).

    Debug.Assert (bIamConsist() = true)

    Debug.Assert (oClass2C.State = adStateOpen)

    'Validación de parámetros, por ejemplo:

    Debug.Assert ( bIsValidParam1(param1) = true)

    'Si es necesario Validación de estados globales, por ejemplo:

    Debug.Assert (DatabaseConnectionG.State = adStateOpen)

 

     'algo de código

     Exit Sub

errCatch:

      'Tratamiento de errores.

End Sub

 

Una posible clasificación de las validaciones es la siguiente:

·         ·        Validaciones Internas. Se escriben para capturar estados erróneas durante el desarrollo.

·         ·        Validaciones Paramétricas.

·         ·        Validaciones Contextuales a nivel de Clase.

·         ·        Validaciones Contextuales a nivel de Aplicación.

·         ·        Validaciones Externas. Se escriben para evitar que se produzcan estados erróneos en la aplicación debidos a entradas no válidas por parte de los componentes externos a la aplicación (usuarios, ficheros, etc).

 

Las únicas validaciones que permanecerán en el producto final son las validaciones externas (sobre todo aquellas concernientes a la interacción con el usuario), las internas tienen como propósito principal el ayudarnos durante el desarrollo del código.

De todas formas, me encuentro con cierta frecuencia con la pregunta de ¿por qué no dejar las validaciones internas? Decir que suponen un alto coste en cuanto a rendimiento, y que no suponen ninguna ventaja para el usuario. Nos ayudan enormemente a desarrollar un código de calidad ya que detectan estados y usos inconsistentes, indicándonos que existe un problema y no teniendo que descubrirlo el usuario.

El tema de las validaciones es tan importante que si es necesario deberíamos mantener información adicional en nuestras clases, módulos, etc, para poder hacer una comprobación más estricta de errores.

 

3.- Reutilización de arquitecturas.

La estructura de la mayoría de las aplicaciones es muy similar. Podemos observar que existen ciertos objetos que usamos en casi todos nuestros programas. Por ello es importante desarrollar una arquitectura clara y sencilla en nuestra aplicación, ayudándonos de desarrollos anteriores, y patrones de diseño.

Haciendo un pequeño apunte sobre una cuestión específica, podemos ver que una categoría importante dentro de los objetos comúnmente usados, se refiere a aquellos que van registrando información que se origina en cualquier parte de nuestra aplicación mientras esta se ejecuta: los objetos Log, los  Reports, e incluso algunos objetos de tratamiento global de errores. Estos han de declararse globales para que puedan ser usados desde cualquier parte de nuestra aplicación. Se recomienda además que tengan una interfaz pequeña y sencilla para evitar dependencias con el resto del código.

 

modMain

Public oLogG As CLogObject

Public oReportG as CReportObject

'....Ser cuidadoso, no abusar de los objetos globales!!!!!

Public Sub main()

    ' Algo de código

    'Inicializar  oLogG y oReportG

    'más código

End Sub

 

Es muy importante no abusar de los objetos globales en nuestras aplicaciones ya que disminuyen la reusabilidad de nuestras clases (las hacemos mucho más interdependientes), de hecho el número de objetos globales debe ser siempre muy reducido. En cualquier diseño uno de los mandamientos a seguir es desarrollar módulos, clases, componentes, etc altamente desacoplados con el resto, por lo que vuelvo a repetir, hacer que estas clases globales tengan un interfaz pequeño, y sólo sean llamadas desde puntos específicos del resto del código.

 

 

4.- Algunos consejos específicos.

 

Existen multitud de cuestiones a tener en cuenta a la hora de hacer una programación de calidad, aquí esbozo algunos temas concretos que he encontrado importantes:

q       q       Uso de manejadores de errores. En cualquier lugar de un programa se pueden producir errores, pero es evidente que hay lugares más propicios para que estos ocurran. En un principio deberíamos de hacer un amplio uso de los manejadores de errores, pero si no es así, no olvidemos implementarlos en el código que haga uso de recursos ajenos a la propia aplicación en sí: interacción con bases de datos, protocolos de comunicaciones, ficheros, etc. De esta forma evitaremos los famosos “Runtime Errors” que hacen que ‘desaparezca’ nuestra aplicación.

q       q       Controlar las dependencias de cada clase. Un inconveniente bastante grande que nos encontramos a la hora de reutilizar una clase en otro proyecto, es que con demasiada frecuencia desconocimos los recursos necesarios para hacerla funcionar, es decir qué componentes utiliza, qué otras clases instancia, etc. Un mecanismo sencillo para controlar esta información consiste en incluir en el comentario cabecera de cada clase una línea en la que especifiquemos claramente todas sus dependencias (componentes que usa, clases que instancia, objetos o variables globales que utiliza, etc).

q       q       Validar la consistencia de clase. Es importante que nos aseguremos que una clase sólo se use si está en un estado consistente. En VB no es obligatorio llamar a un constructor con lo que debemos asegurarnos no solo de tener uno (aunque esté casi vacío) sino obligar a usarlo. Podemos también encontrarnos con clases cuyo proceso de instanciación esté gradado o se da en varios pasos. Tener una variable de clase que nos indique si la clase se haya en un estado consistente (es decir si ha sido iniciada con todos los datos y referencias que necesita) es algo que puede ahorrarnos muchos problemas. Por lo general yo denomino a esta variable ‘bConsistC’.

q       q       Mantener la unicidad funcional. Hacer que las funciones que construyamos sirvan para una única cosa. Esto hace más fácil reutilizarlas, depurarlas, construir aserciones para ellas, entenderlas y comunicarlas.

q       q       No incurrir en el desnivel semántico. El código básicamente se estructura en bloques de dos tipos: funciones y procedimientos. Debemos de procurar que el nivel de las abstracción que manipulemos estén en un nivel de abstracción similar. Esto favorece la creación de código claro, muy autoexpresivo y libre de errores, además de que es más fácil de entender y depurar. Lo contrario, es incurrir en lo que  llamo desnivel semántico.

 

 

 

5.- Algunos otras reglas generales.

 

Para finalizar este repaso informal sobre la práctica de la programación apuntamos algunos consejos generales sobre el desarrollo de código de calidad, que no debemos perder de vista.

 

q       q       Seguimientos de código. No escatimemos esfuerzos en contemplar como funciona nuestro código. Hoy en día la mayoría de los entornos de desarrollos permiten hacer tareas de seguimiento de una forma sencilla y rica. Es increíble la cantidad de errores que uno es capaz de encontrar viendo funcionar el código paso a paso, ya que en esta situación se invierte la relación entre el programador y el ordenador, siendo el cerebro capaz de pensar más rápido que él, y ver más allá de lo que con anterioridad hemos escrito.

q       q       Conseguir que el código tenga una visualización correcta. Hacerlo más legible. Esto se consigue formateando bien el código: sangrado, colores, estructuración, tamaño adecuado de líneas, extensión correcta en funciones, procedimientos, clases, etc.

 

¿Cuál es más fácil de leer?

1.-

Public Sub Procedure(parameters list)

On Error GoTo errCatch

Debug.Assert (ValidateSomething())

If bSomeCondition = False Then

DoSomething

Else

For each object in colObjects

If bSomeOtherCondition = false then

DoOtherThings

End If

Next

End If

Exit Sub

errCatch:

MsgBox Err.Description, .......

End Sub

 

2.-

Public Sub Procedure(parameters list)

On Error GoTo errCatch

    Debug.Assert (ValidateSomething())

    If bSomeCondition = False Then

        DoSomething

    Else

        For each object in colObjects

            If bSomeOtherCondition = false then

                DoOtherThings

            End If

        Next

    End If

    Exit Sub

errCatch:

    MsgBox Err.Description, .......

End Sub

 

q       q       Construir interfaces gráficos consistentes. Es importante situar al usuario ante lo conocido, y por ello no debemos temer el ‘copiar’ el estilo de aplicaciones comúnmente usadas que sitúan rápidamente a nuestros usuarios sobre la pista de cómo deben de interaccionar con la aplicación. Es importante usar estructuras visuales que reflejen aquello que se está manejando (las estructuras que están por abajo). Esto hace que el usuario pueda olvidarse que existe una aplicación entre él y sus datos.

q       q       Centrarse en lo importante. No todas las opciones de un programa tienen igual importancia, con lo que no debemos de olvidar asegurarnos que aquello que de verdad tiene que hacer una aplicación lo haga bien.

q       q       De forma general, no sacrificar claridad por velocidad. La velocidad depende básicamente de la arquitectura y tecnología usada en una aplicación y no de detalles concretos que hayamos desarrollado en nuestro programa.

 

Bibliografía:

 

  • ·        Maguire, Steve. “Código Sin Errores”. McGraw-Hill, Microsoft Press (1994).
  • ·        Gamma, E.,  Helm, R.,  Johnson, R.,  Vlissides, J. “Design Patterns. Elements of Reusable Object-Oriented Software”, Addison-Wesley, 1994.
  • ·        Meyer, Bertrand. “Construcción de Software Orientado a Objetos”. Prentice may, 1999.
  • ·        Hamilton, J.P. “Fifty Ways to Improve Your Visual Basic Programs”, http://vb.oreilly.com/news/vbshell2_0901.html .
  • ·        Díaz Toledano, Moisés Daniel. “Principios de Diseño Web”, http://www.moisesdaniel.com/es/wri/PrincDisWeb.html .

 

 

 

 

Moisés Daniel Díaz, Ingeniero en Informática.

Web: www.moisesdaniel.com