Parece ser que una de las causas es la poca claridad en los requerimientos definidos por el cliente quien muchas veces es exigente pero muy laxo en cuánto a expresar qué es lo que pretende del software que necesita. Eso provoca la falta de claridad en los requerimientos lo cual motiva que muchas decisiones sobre la interpretación del dominio queden en manos de los desarrolladores quienes tienen que imaginar (inclusive pueden llegar a recrear en su cabeza una imagen mítica de un Owner que jamás vieron :) ) cómo serian las cosas en el negocio del cliente, pero hagámonos la idea que tendríamos que ponernos como “expertos del negocio” y comenzar definir cosas (podríamos hacerlo muy bien?). Ojo que esto no tiene que ver con ponerle limites al cliente respecto a lo técnico, sino que me refiero a que él debería ayudar a definir lo mejor posible los requisitos sino nos veremos en problemas a largo plazo.
Por otro lado, la otra causa es que en general , ya sea por falta de skills, porque no hay ganas, porque no queremos poner “cabeza” (lo cual suele pasar), porque estamos en modo “conformistas” o por lo que sea no respetamos lo que se conocen como “Principios de Diseño” los cuales son guías y hasta patrones que nos dicen, en un mundo OO, cómo deberíamos organizar y escribir nuestras clases, métodos, propiedades con el fin de que la mantenibilidad vea la luz.
Por decirlo de otro modo: existen unos cuantos principios (recomendaciones) que si son apropiada y extensivamente aplicados transformarían una pieza de código en una manejable y flexible.
Es importante demarcar que este tipo de código que carece signos claros de un diseño inteligente junto con comportamientos y datos duplicados además de el conocido código spaguetti, se conoce como la Gran Bola de Barro (Big Ball of Mud).
Los síntomas usuales de que tenemos una Gran Bola de Barro son estos:
- Al hacer un cambio en un lugar, se rompe el código en otra: un cambio en un modulo repercute en cascada hacia otros módulos, lo cual provoca que sea muy difícil predecir el impacto de un cambio.
- Fácil de usar pero difícil de reusar: aquí el problema es la cantidad y profundidad de las dependencias inadecuadas entre módulos. Cuando usar un modulo se vuelve complicado, en general se llega a la opción de reescribir el código desde cero (from scratch), porque entenderlo es duro.
- Más fácil de aplicar un solución temporal (workaround) que resolver de acuerdo al diseño: un workaround es una solución de compromiso (“después lo arreglamos mejor”) para un problema puntual del software porque hacer una solución (fix) exacta “de acuerdo al diseño” llevaría mucho tiempo (para entender y ver como encajan las piezas).
Lo usual que todos conocemos desde la facultad, son los patrones GRASP (object-oriented design General Responsibility Assignment Software Patterns) los cuales nos dan las sugerencias básicas sobre cómo asignar responsabilidades (qué debe hacer cada objeto) para un diseño de software adecuado y claro. Estas buenas practicas son importantes y deberíamos tenerlas en cuenta para lo que sigue.
Dejo un grafico esquemático, no lo vamos a repasar porque se van a ver implícitos en lo que desarrollaremos un poco más adelante, pero vale la pena recordarlos:
Alta Cohesión
La cohesión quiere decir que todo el código de un modulo (entiéndase como una clase o método o un assembly) debe estar lógicamente relacionado. La cohesión se mide de Alto a Bajo y siempre se prefiere que un modulo tenga una Alta Cohesión.
Ejemplo de baja cohesión (indeseable)
1: List<Proveedor> FindProveedoresByNombre(string nombre)
2: {
3: var contentXML = System.IO.File.ReadAllText("config.xml");
4: //aqui se busca la conexion en el archivo leido;
5: var connectionStr = "....";
6: SqlConnection sqlConn = new SqlConnection(connectionStr);
7: var cmd = sqlConn.CreateCommand();
8: cmd.CommandText = "SELECT * from proveedor where nombre like '%" + nombre + "'%";
9: var sqlReader=cmd.ExecuteReader();
10:
11: List<Proveedor> resultados = new List<Proveedor>();
12: var counter=0;
13: while (sqlReader.Read())
14: {
15: // mapear objeto de salida
16: counter++;
17: }
18:
19: sqlReader.Close();
20:
21: //Aqui se envia un mail con la cantidad de resultados.
22: var body = string.Format("Se encontraron {0} proveedores con nombre'{1}'", counter, nombre);
23: mail.Send(body);
24:
25: return resultados;
26: }
Este método como está escrito tiene muy baja cohesión porque su fin es buscar proveedores con un criterio especifico pero hace de todo: busca el archivo de configuración, extrae la cadena de conexión, crea la conexión a la base de datos, elabora la query SQL, ejecuta, mapea y encima envía un mail!!. Pésimo, se pierde totalmente la coherencia porque si bien son tareas que se requieren para buscar un proveedor, estas son demasiadas responsabilidades para un solo método. Imaginemos que después tenemos otro tipo de filtro que requerirá la misma cadena de conexión, entonces vamos a repetir el mismo bloque de código? De eso se trata la alta cohesión: hacer que un método se concentre en una tarea lo mas especifica posible.
Bajo Acoplamiento
El acoplamiento tiene que ver con el grado de dependencia de dos módulos lo cual implica que si A depende excesivamente de B, entonces un cambio en B puede provocar efectos colaterales en A. El acoplamiento dice que que si la superficie de contacto de dos módulos es menor, es mucho mejor. El bajo acoplamiento esta relacionado con el ocultamiento de información y las abstracciones: mientras más ocultos esten sus detalles de implementación y más clara y estables sean sus puntos de contacto con el exterior (interface), menor será el acoplamiento. Es característica es siempre lo deseable.
Ejemplo de alto acoplamiento (indeseable)
1: class Articulo
2: {
3: public decimal precio;
4: public int cantidad;
5: public string descripcion;
6: }
7:
8: class Inventario
9: {
10: public Articulo[] Articulos;
11: }
12:
13: class InventarioValorado
14: {
15: private decimal impuesto;
16: private Inventario inventario;
17:
18: public InventarioValorado(decimal _impuesto,Inventario _inventario)
19: {
20: this.inventario = _inventario;
21: this.impuesto = _impuesto;
22: }
23:
24: public decimal CalcularTotal()
25: {
26: decimal total = 0;
27: //esta invadiendo el ocultamiento de los detalles de implementacion.
28: foreach (var item in this.inventario.Articulos)
29: {
30: var subtotal = item.precio * item.cantidad;
31: var subtotalConImpuesto = subtotal * (1 + subtotal * (item.cantidad * 0.02m * impuesto));
32: total += subtotalConImpuesto;
33: }
34:
35: return total;
36: }
37: }
En este ejemplo, la clase “InventarioValorizado” tiene un alto acoplamiento a las clases Articulo e Inventario porque depende de sus detalles internos (tiene que saber que guarda en un array los artículos) y además InventarioValorizado esta calculando la valorización de cada articulo (esto debería saberlo el Articulo, no?). Esto es indeseable.
Separación de intereses (SC)
Este principio es clave para conseguir la Alta Cohesión y el Bajo Acoplamiento. La separación de intereses (Separation of Concerns) nos dice que hay que dividir un sistema en features que no se solapen. Lo importante es que cada feature represente un “concern” o “aspecto” de nuestro sistema de manera que disminuyamos considerablemente la duplicación de funcionalidades. Cada concern se mapea en módulos.
Mencionaré al paper “On the role of scientific thought” del prócer de la informática como fue Edsger W. Dijkstra, el ensayo introduce la noción de separación de intereses |
SC nos dice que en un momento dado nos concentremos en un aspecto del sistema a la vez, ignorando temporalmente los demás.
SC se consigue a partir de:
Modularidad: cada feature tendrá su propio modulo con una interfaz pública fija y estable con la que se comunicará con otros módulos. Se relaciona con el Ocultamiento de la Información.
Ocultamiento de la información: significa que detrás de una interfaz estable se ocultan detalles, sujetos a variación, sobre cómo esta implementado (programado) un modulo. Así, los módulos se atan a una interfaz fija y no serían afectados por los cambios en la implementación.
Un ejemplo práctico de ocultamiento de la información es el de las propiedades (property) de C#:
Finalmente dejo una observación que es importante y clara: SC induce a la aplicación de la alta cohesión, ya que separar aspectos implica dividir en responsabilidades únicas.
En el siguiente post veremos los Principios SOLID, los que se derivan como una consecuencia de los Principios universales de diseño.
![]() |
Proximo Post... |
No hay comentarios.:
Publicar un comentario