Conceptos Básicos en Ingeniería de Software III

Conceptos de alto nivel

Ya hemos visto la conveniencia de los lenguajes orientados a objetos en lo que refiere al desarrollo de software de buena calidad. En efecto estos lenguajes proporcionan herramientas muy útiles que facilitan el desarrollo e implementación, pero no hay que perder de vista que en definitiva el éxito final depende del desarrollador y no del lenguaje; que de echo un buen programa en C puede ser de mucho mejor calidad que uno malo en C++. El lenguaje es mas un empleado que un socio del programador, esto es: "Trabaja para este y no por este", así que tenemos antes de pasar al diseño en si, que precisar algunos elementos de mas alto nivel que hacen al tipo de software que queremos desarrollar. Algunos ya han sido mencionados al pasar, pero sin irnos por las ramas vale la pena hablar algo mas en detalle de cada uno.


Una Biblioteca

Cuando uno habla de "Biblioteca" enseguida vienen a la cabeza las viejas bibliotecas de algoritmos matemáticos que mantienen su vigencia, aún después de muchos años de haber sido concebidas. Una biblioteca es un conjunto de recursos (erg. estructuras de datos, funciones, etc.)  que el usuario puede usar en sus programas. El efecto de incluir una biblioteca es básicamente extender el conjunto de recursos que provee el lenguaje, sin ninguna lógica o jerarquía subyacente, sencillamente agrego componentes nuevos a este.
Una de las grandes ventajas de las bibliotecas es su uso simple cuando existen conceptos bien identificados en el dominio de aplicaciones y el re-uso sea de algoritmos sobre datos simples.
Ejemplos de bibliotecas exitosas son: "Las de Algoritmos Matemáticos" , "Bibliotecas Tecnológicas (erg.: funciones como hora, fecha, etc.)", "De Interfaces de Usuario" o "Estructuras Abstractas de Datos y Algoritmos". En todos los ejemplos anteriores existe un común denominador: "La aplicación es construida sobre la biblioteca en cuestión" o sea el usuario es responsable de diseñar la arquitectura y la biblioteca de proveer componentes estándar de código para ser usados. En una enorme y diversa cantidad de problemas pueden identificarse abstracciones matemáticas tales como: "Ecuaciones Lineales", "Problemas de Optimización", etc. que pueden ser resueltos sin importar demasiado el tipo de problema real en si que hay detrás. Esta es la razón por la cual las bibliotecas matemáticas han tenido tanto éxito; son generales y pueden ser complementadas fácilmente con otras. Pensemos en Matlab que esencialmente es una biblioteca de algoritmos matemáticos construida sobre dos entidades básicas: Matrices y Funciones. Sin duda este y otros paquetes deben su éxito a su enorme flexibilidad y fácil uso, gracias a los cuales rápidamente es posible prototipar diversas aplicaciones en áreas muy disímiles.
Hasta aquí uno podría erróneamente creer que una biblioteca es la mejor inversión en desarrollo de software, basados en la idea que la parte mas compleja de este es la codificación de algoritmos y datos. Antes de argumentar que lo anterior no es siempre cierto, observemos que los ejemplos exitosos no incluyen: "Bibliotecas para Dominios Específicos de Aplicaciones" y es razonable preguntarse... ¿Por Que?. La razones básicas son:
Efectivamente, cuando un equipo de desarrollo trabaja sobre un dominio de aplicación inmediato concreto (un biblioteca de matemáticas también tiene un dominio concreto pero no esta concebida para crear aplicaciones directamente, sino para ser soporte de otras) una biblioteca implica repensar constantemente una gran porción de sus aplicaciones, con la consecuente ineficiencia de esto.  El problema de re-uso de código puede sortearse extendiendo el conjunto de recursos de la biblioteca periódicamente, tarea que puede resultar mucho mas compleja de lo previsto, dado el enorme grado de caos al que se puede llegar, con un crecimiento naturalmente desorganizado e inspirado en base a la experiencia de fugaces grupos de trabajo,  en la mayoría de los casos inspirados en los problemas concretos de su proyecto.
Existe además el obstáculo insalvable del bajo re-uso de arquitectura, sumamente importante cuando esta en sí es mas compleja que la implementación súper optimizada de un algoritmo concreto.
En resumen; si el tipo de aplicaciones que estamos desarrollando son de naturaleza muy diversa y siempre pueden reducirse a interacciones entre un puñado de objetos bien definidos, seguramente una biblioteca sea la mejor alternativa de desarrollo. Cuando estas no sean las condiciones de trabajo posiblemente convenga estudiar el siguiente punto.
Insistimos que el fracaso de la vieja biblioteca de imágenes, se debió al echo de ser una biblioteca en si; dado que debía funcionar sobre un dominio de aplicaciones bien definido y se esperaba fuera enriquecida con el aporte de proyectos concretos de grupos de estudiantes, con el consecuente desorden en el crecimiento, que termino matándola al tener esta que soportar un tamaño enorme con una estructura débil y rígida.

Un Framework

Según Johnson & Foote, un Framework es: "Un conjunto de clases que incorporan un diseño abstracto para soluciones a una familia de problemas relacionados, y soporta re-uso a un nivel de granularidad mayor que el de las clases". Como la mayoría de las definiciones formales, la anterior deja perfectamente claro de que se esta hablando, para una persona con experiencia en el tema.
Un framework incorpora las partes esenciales de una familia de aplicaciones, incluyendo la lógica misma. Consiste de muchas clases que a diferencia de una biblioteca, colaboran según un orden preestablecido que por un lado la limita en cuanto al tipo de aplicación a desarrollar, al mismo tiempo que hace posible verla como una solución genérica a un conjunto de aplicaciones.
Una biblioteca y un framework están concebidas para distintos usuarios. La primera puede ser usada por cualquiera que necesite el tipo de objetos o algoritmos que esta presta como recursos,  mientras que la segunda debe usarse para desarrollar una aplicación concreta del dominio. Insistiremos sobre el uso del framework ya que es este el que deja mas clara la diferencia con una biblioteca.
Es útil pensar en una biblioteca como un gran paquete de piezas (recursos), con las que el usuario arma un rompecabezas determinado (su aplicación), existiendo claro esta muchísimas formas distintas de hacerlo.
Un framework tiene una concepción radicalmente distinta, primero identifica las partes variables de una aplicación a otra del dominio ( en la jerga: Hot Spots )  y luego de definir su interface ( forma en el dibujo ), arma el resto del rompecabezas de modo estático a menos de un vacío en cada Hot Spot, para que el usuario construya o elija uno adecuado a su aplicación concreta.
El equivalente gráfico expone en forma clara la diferencia en el uso de ambos tipos de paquete. Mientras en una biblioteca el programador construye la aplicación propiamente dicha en general el "main" del programa, en un framework este está básicamente establecido y el usuario solo es responsable de codificar las porciones distribuidas a través de la arquitectura que varían de una aplicación a otra. En otras palabras desarrollar en base a una biblioteca equivale a edificar una arquitectura montada sobre esta, mientras que hacerlo en un framework implica trabajar dentro (en general bajo) de este. Como consecuencia una integración consistente de nuevo código al framework, es una integración efectiva y el mismo pasa estar disponible en su uso a los usuarios futuros. Destacamos que:
También hay que destacar que el uso de un framework requiere un conocimiento más detallado de la arquitectura, o sea, el usuario de un framework no puede pensar en sentarse a programar sin haber leído previamente y en detalle, la documentación del sistema. A nuestro entender esta es una buena inversión de tiempo para una persona que desarrolle software en un área específica ( como tratamiento de imágenes ), ya que el diseño tiene resueltos la mayoría de los problemas de esta. De todos modos en BICOTI algo fue alivianado a través de las "fachadas".

Por último no está de más comentar que existen dos tipos de frameworks:

En el primer caso el programador debe crear clases concretas para un conjunto de objetos de los que solo está definida la interface. Son muy generales, aunque puede ser muy tedioso generar código de una aplicación a otra sobre todo si suelen aparecer porciones en común, usadas de distinto forma. El usuario en general no crea un "main" propiamente dicho. Un ejemplo conocido de este tipo de framework, que suele emplearse en desarrollo de aplicaciones son las MFC's del "Microsoft Visual C++".

En un framework caja negra, existen muchas alternativas implementadas para los hot spots y su uso se reduce (aunque no se limita), a configurar el framework eligiendo la combinación adecuada al caso. Es importante destacar que aún con pocas implementaciones de hot spots pueden conseguirse muchas aplicaciones distintas, ya que estas dependen del número de combinaciones. De lo anterior este mecanismo de re-uso de código tiene un orden casi exponencial, en contraste con lo que una biblioteca ofrecía. El usuario en general crea un main, donde configura el framework. Esperamos que Bicoti sea un buen ejemplo de un framework caja negra.
 

Design Patterns

En los hechos es una tarea muy difícil desarrollar código orientado a objetos re-usable y en general son los desarrolladores experimentados los que logran resultados exitosos. Es pertinente entonces preguntarse ¿Que diferencia a los desarrolladores experimentados de los novatos?. En realidad existen innumerables factores pero en este punto nos interesa destacar uno en particular: "El desarrollador experimentado no desarrolla sus aplicaciones desde cero, ya que re-usa buenas soluciones previas de otros problemas". A medida que afronta proyectos, el desarrollador va identificando ciertos "Patrones" que pueden ser re-usados.

Design Patterns documenta un breve pero importante conjunto de soluciones estándar a problemas comunes de diseño, en un esfuerzo por compartir experiencias entre desarrolladores experimentados, al mismo tiempo que acelera el proceso de aprendizaje de los más novatos.

No hay nada mejor que referirse a la fuente para lograr una idea más cabal de en que consisten. De hecho el uso de componentes estándar descentraliza la documentación permitiendo re-usar esta también, así que nos limitaremos a decir que en nuestra arquitectura identificamos los siguientes patterns dando además algun ejemplo:

Recomendamos al usuario consultar la bibliografía correspondiente (ver referencias bibliográficas: Design Patterns).

Standard Template Library

El uso de componentes estándar es la primera forma natural de re-uso que facilita la tarea del desarrollador de software, dándole un perfil más compatible con el resto de las ramas de la ingeniería, en las cuales los ingenieros usan constantemente componentes de uso estándar para llevar a cavo sus proyectos. Lamentablemente en la Ingeniería de Software, este tipo de logros han sido muy limitados. Aparte de las clásicas bibliotecas de C/C++, en bicoti incorporamos componentes estándar de dos tipos. El primero a nivel de diseño de arquitectura son los Dessign Patterns vistos en el punto anterior. A un nivel mucho más bajo hacemos uso de una biblioteca que es parte del estándar desde hace poco tiempo, la STL.

Brevemente diremos que esta biblioteca provee un conjunto de estructuras de datos y algoritmos sumamente: flexibles, eficientes y confiables, que facilitan enormemente la implementación de la mayoría de los algoritmos, al mismo tiempo que mejoran la claridad del código y evitan lidiar con problemas molestos, entre otros el manejo y fragmentación de la memoria. Muchas ventajas sin duda; sin embargo la STL tiene un defecto importante, uno debe invertir tiempo en aprender a usarla antes de poder hacer algo productivo con ella.

Existe abundante docuementación al respecto así que no haremos un curso aquí, simplemente citaremos dos papers que nos ayudaron mucho durante nuestro período de aprendizaje.

Como comentario final y tal cual veremos en el siguiente punto, bicoti está construída en base a la stl y en consecuencia es improbable que un usuario pueda hacer algo más que prototipar alguna aplicación, sin previamente haber aprendido las bases en el uso de esta.

La stl se volvió estándar reconocido en paralelo con el desarrollo de nuestro proyecto; en consecuencia se decidió re-escribir la porción del código que ya teníamos implementada y funcionando, así como todo el que agregáramos en el futurio. Tiempo mediante,  los integrantes del grupo hemos encontrado que esta decisión fue excelente y que el costo ahorrado desde el momento en que fue tomada hacia adelante, compensó con creces el consumido en cambiar lo que estaba hecho. Aprender a usar la stl va mucho más allá de bicoti mismo, es un paso ineludible para cualquier persona con intenciones de programar en C++.