archivo

Archivo de la etiqueta: programación orientada a objetos

La torre de vudú como antipatrón es consecuencia de la siguiente práctica de programación:

Se tiene un código que se sabe que funciona (aunque generalmente no se sabe muy bien cómo) y se pretende añadir algún tipo de funcionalidad adicional, en ocasiones no muy cohesionada con la ya implementada y se le coloca un envoltorio (wrapper) proporcionando una nueva interfaz de acceso a ese nuevo componente. Se puede considerar, por tanto, una extensión del antipatrón “Voodoo chicken coding“.

Recibe el nombre de torre de vudú porque todo parte de un código correcto a la que se le van añadiendo diferentes capas de abstracción que van proporcionando funcionalidades adicionales a la original, de manera que al final tenemos un módulo que puede funcionar pero que tiene como punto débil la parte de código que no comprendemos de manera adecuada (y que está en la base de la torre, es decir, fue la semilla a partir de la cuál se construyó la misma) y/o de código que a través de sucesivas abstracciones resulta complicado de entender al ir extendiéndose su funcionalidad para realizar tareas muy diferentes a la inicialmente prevista.

Existe la tentación, cuando se hace uso de la técnica de tarjetas CRC de aprovechar e incluir en la misma la implementación de la clase (aunque sea en pseudocódigo), convirtiéndose automáticamente la tarjeta CRC (Class-Responsibility-Collaboration) en CRCI (Class-Responsibility-Collaboration-Implementation).

Pues bien, esa práctica se considera un antipatrón, ya que se considera que se puede estar perdiendo el tiempo realizando la implementación de una clase, cuando todavía no está totalmente diseñado el sistema y por tanto no se tiene una visión todavía a nivel de detalle del mismo, lo que obligará más que probablemente a tener que rehacer la implementación especificada en la tarjeta en reiteradas ocasiones.

La utilización de tarjetas CRC (Class-Responsibility-Collaboration) es una técnica de diseño orientado a objetos propuesta por Kent Beck (introductor de la metodología de programación extrema) y Ward Cunningham (también muy conocido entre otras muchas materias, por sus aportaciones a dicha metodología).

El objetivo de la misma es hacer, mediante tarjetas, un inventario de las clases que vamos a necesitar para implementar el sistema y la forma en que van a interactuar, de esta forma se pretende facilitar el análisis y discusión de las mismas por parte de varios actores del equipo de proyecto con el objeto de que el diseño sea lo más simple posible verificando las especificaciones del sistema.

Un esquema típico de tarjeta CRC puede ser aquel en el que se indiquen los siguientes datos:

- Nombre de la clase.
– Nombre de las superclases y subclases (si procede).
– Las responsabilidades de la clase.
– Las clases con las que va a colaborar para poder realizar las responsabilidades indicadas.
– Autor, fecha, etc…

Este antipatrón se basa en la utilización dentro de un mismo sistema de diferentes “clases de propósito general”, “clases hombre-orquesta” o “clases multicasuística” en las cuales están definidos una amplia gama de comportamientos con el objeto de poder ser utilizados por otras.

También es frecuente encontrarlo en implementaciones de componentes en los que se delegan funcionalidades de otros, con el objeto de dar un amplio rango de alternativas posibles al cliente.

El problema no es dar esas facilidades, sino que el problema se encuentra en la complejidad que se le añade al código como consecuencia de las mismas (generalmente alto acoplamiento, baja cohesión, alta complejidad ciclomática, etc…).

En el antipatrón “objeto todopoderoso“, la funcionalidad de la aplicación pivota generalmente sobre una de estas clases, mientras que en la navaja suiza se trata de diferentes clases (generalmente, de utilidad) que contiene el sistema.

Responsibility-driven design es una técnica de diseño orientado a objetos propuesta por Rebecca J. Wirfs-Brock and Brian Wilkerson.

Rebecca J. Wirfs-Brock es una autora y consultora americana especializada en los ámbitos del diseño y programación orientada a objetos, con una gran experiencia desarrollada en diferentes empresas.

Brian Wilkerson es otro especialista en programación y diseño orientado a objetos, que ha desarrollado su carrera profesional en el mundo de la consultoría.

A principio de los 90 fueron coautores de los libros en los que formulaban esta técnica de diseño.

El diseño orientado a la responsabilidad se basa en dar respuesta para cada tipo de objeto a las siguientes preguntas:

- ¿De qué acciones es responsable el objeto?
– ¿Qué información comparte el objeto?

Proporcionando una alternativa a los diseños que consideraban a los objetos como datos y algoritmos. En este caso los objetos se consideran como la conjunción de roles y responsabilidades, los cuales conviven cooperando dentro de la aplicación.

Esta técnica de diseño está claramente inspirada en la mecánica de funcionamiento cliente/servidor, en la cual se define un contrato que rige el funcionamiento de los objetos implicados de manera que el cliente solo puede realizar los tipos de solicitudes/peticiones que se hayan definido y el servidor debe responder.

Es una técnica que también huye del conocimiento de los detalles de cómo un objeto realiza una acción u obtiene un resultado, fomentando de esta forma el concepto de encapsulamiento. Además, para reforzar esta cualidad, Wirfs-Brock y Wilkerson abogan por la existencia de un control de grano fino de visibilidad de los objetos.

No solo es una técnica que persigue el encapsulamiento sino la abstracción al ocultar, al menos en términos de diseño, la relación entre datos y comportamiento, ya que solo se debe pensar en responsabilidades a nivel de conocer, hacer y decidir.

En la técnica, por tanto, tendremos una comunidad de objetos que tienen asignadas responsabilidades específicas y que conforman un modelo colaborativo de funcionamiento en el que los mecanismos de colaboración están claramente definidos constituyendo de esta forma una arquitectura en la que existe un comportamiento distribuido.

De esta forma podemos realizar las siguientes definiciones:

¿Qué es una aplicación? Un conjunto de objetos que interactúan.
¿Qué es un objeto? La implementación de uno o más roles.
¿Qué es un rol? Un conjunto de responsabilidades
¿Qué es una responsabilidad? La obligación de realizar una tarea o de conocer un determinado tipo de información.
¿Qué es una colaboración? A la interección entre objetos y/o roles.

Tal y como se ha comentado anteriormente, esta técnica considera a los objetos como algo más que paquetes que solo encierran lógica y datos, ahora se en función de los roles que desempeñen a controladores, proveedores de servicios, titulares de información, interfaces con el exterior, etc…

Una vez analizados los principios y valores del manifiesto ágil, en este artículo se va a realizar un breve perfil de sus diecisiete participantes, con el objetivo de que se pueda apreciar que todo el equipo de personas que participaron eran profesionales de reconocido prestigio y que en su trayectoria profesional tenían experiencia e incluso habían publicado artículos y libros sobre la aplicación de estrategias que posteriormente a alto nivel se plasmaron en el manifiesto ágil.

- Kent Beck: Americano. Creador de la metodología de programación extrema y de la metodología de desarrollo guiado por las pruebas (TDD: Test-driven Development). Coautor de JUnit. Promotor de la reunión que dio lugar a la creación de manifiesto ágil para el desarrollo de software.

- Mike Beedle: Americano. Ha sido uno de los pioneros en la aplicación de Scrum (desde 1995) en proyectos de desarrollo de software en diferentes organizaciones y tipos de sistemas (en cuanto al proceso o procesos de negocio que tratan de informatizar).

- Arie van Bennekum: Holandés. Jefe de proyectos y consultor especializado en la metodología DSDM (método de desarrollo de sistemas dinámicos), representó al DSDM Consortium en la reunión.

- Alistair Cockburn: Americano. Creador de la familia de metodologías Crystal. Contribuyó a la expansión de la utilización de los casos de uso con la definición que hizo de los mismos en el libro “Escribir casos de uso efectivos” publicado en el año 2000, propiciando la generalización de su uso para describir el comportamiento de los requisitos software y de los procesos de negocio (a más alto nivel). La creación del concepto de caso de uso es bastante anterior, del año 1986 y fue realizada por el sueco Ivar Jacobson

- Ward Cunningham: Americano. Howard G. Cunningham fue el creador de la primera solución Wiki (WikiWikiWeb) y se considera un pionero tanto en la aplicación de programación extrema como en la utilización de patrones de diseño.

- Martin Fowler: Americano. Especialista en procesos de refactorización de software, programación orientada a objetos, UML y programación extrema. Hizo popular el uso del término “inyección de dependencias”.

- James Grenning: Americano. Especialista en sistemas empotrados. Fue uno de los pioneros en desarrollo de proyectos bajo la metodología de programación extrema. Experto en TDD (Test-driven Development). Ha sido el creador de la técnica de estimación Planning Poker.

- Jim Highsmith: Americano. James A. Highsmith III. Ingeniero de software experimentado que ha participado en proyectos de desarrollo de software en muchos países del mundo. En el año 1999 publicó el libro “Adaptative Software Development” donde puso de manifiesto la naturaleza adaptativa de los proyectos de desarrollo de software. Se ha especializado en la consultoría de proyectos que se realizan en entornos inestables y con una complejidad creciente.

- Andrew Hunt: Americano. Desarrollador de software con gran experiencia. Publicó junto a Dave Thomas en el año 1999 el libro “The Pragmatic Programmer”, en el que realizó una definición de lo que consideraban un programador práctico o pragmático:

- Early adopter.
– Curioso.
– Pensador crítico.
– Realista.
– Aprendiz de todo, maestro de nada.

Fue uno de los introductores en occidente de la programación en Ruby, mediante la publicación en el año 2000 del libro “Programming Ruby: A Pragmatic Programmer’s Guide”.

- Ron Jeffries: Considerado junto a Kent Beck y Ward Cunningham como uno de los creadores de la programación extrema. Participó junto a Beck en el proyecto de 1996 en Chrysler que dio lugar a este concepto.

- Brian Marick: Americano. Especialista en realización de tareas de testing. En el año 1995 publicó uno de sus libros más conocidos: “The Craft of Software Testing: Subsystem Testing Including Object-based and Object-oriented Testing”. Experto en Ruby.

- Robert C. Martin: Americano. Robert Cecil Martin (Uncle Bob). Dedicado a tareas de desarrollo de software desde 1970. Especialista en programación orientada a objetos sobre todo en el área de patrones de diseño.

- Steve Mellor: Americano. Especialista en sistemas empotrados, programación orientada a objetos y MDA (Model Driven Architecture). Creador junto a Sally Shlaer del método Shlaer-Mellor que es uno de los métodos de análisis y diseño orientado a objetos que surgió a finales de la década de los ochenta como respuesta a las principales problemáticas que presentaba el análisis y diseño estructurado, entre las cuales destacaban la complejidad de los diseños estructurados y la dificultad de mantener tanto su documentación de análisis como de diseño.

- Jon Kern: Americano. Inició su carrera profesional en sistemas empotrados en el área militar. Considerado como uno de los pioneros en la aplicación de la programación orientada a objetos. Especialista en programación Java y en MDA.

- Ken Schwaber y Jeff Sutherland: Americanos. Creadores de la metodología Scrum.

- Dave Thomas: Inglés. Autor junto a Andrew Hunt del libro “The Pragmatic Programmer”. También junto al mismo autor se considera uno de los introductores de Ruby en occidente.

Ralph E. Johnson es profesor universitario y especialista en la utilización de patrones de diseño y de la programación orientada a objetos.

Precisamente por ser esa su especialidad, resulta llamativa su siguiente cita (traducción libre): “Antes de que el software sea reutilizable primero debe ser utilizable”.

Y es verdad, de nada sirve un software técnicamente perfecto si después no hace lo que el usuario quiere. Si bien todo no es ni blanco ni negro, ya que no solo se debe buscar el cumplimiento de objetivos funcionales sino que también hay que pensar que el producto software va a requerir de sucesivas evoluciones y las mismas serán tan costosas como malo haya sido el diseño y la codificación (la deuda técnica).

El puglin de Sonar Quality Index proporciona otra métrica agregada (resultado de otras métricas) para obtener un valor que indique la calidad del software. En este caso lo que se pretende es obtener un valor entre 0 y 10.

Particularmente me gusta más SIG MM ya que se centra en la mantenibilidad que es lo que realmente me preocupa sobre el software que nos entregan, pero todo es cuestión de gustos y de lo que cada uno valore lo que te calcula la métrica y lo claro que te exprese los resultados. Desde mi punto de vista este indicador da demasiado peso a la verificación de reglas PMD y pone casi al mismo nivel la verificación de estilos que la complejidad ciclomática y no otorga ningún valor al acoplamiento o a la cohesión (algo de lo que también adolece SIG MM).

Para su cálculo se basa en la obtención previamente de cuatro métricas:

- Violaciones de código: basada en los resultados obtenidos mediante la aplicación de las reglas PMD y se calcula a partir de la siguiente fórmula:

Coding Violations = (Blocker * 10 + Critical * 5 + Major * 3 + Minor + Info) / validLines

donde validLines es el número total de líneas de código de la aplicación menos el código duplicado. Como puede darse el caso de que la métrica código duplicado supere al número de líneas de código cuando ocurre esto el valor de validLines es 1. Como casi todas las métricas de Quality Index se hacen en relación al número de líneas válidas la influencia de la duplicidad de código en los resultados finales es importante (como también lo es en el cálculo de otras métricas como Technical Debt o SIG MM, algo que me parece totalmente razonable. Esto va a provocar en consecuencia que soluciones basadas en generadores de código, salvo que éstos estén muy depurados van a verse muy perjudicadas por esta métrica.

- Complejidad: basada en los resultados obtenidos mediante la obtención de la complejidad ciclomática de los diferentes métodos y se calcula a partir de la siguiente fórmula:

Complexity = (Complexity>30 *10 + Complexity>20 * 5 + Complexity>10 * 3 + Complexity>1) / validLines

Como se puede apreciar se le da un peso importante a métodos con una complejidad ciclomática alta que son aquellos que al presentar un mayor número posible de caminos lógicos hace que sean más complicados de mantener

- Cobertura: Se obtiene directamente a partir de la cobertura de código por pruebas unitarias (mirando un comentario del código del plugin (clase CoverageDecorator.java) no tiene en cuenta para el cálculo de esta métrica las líneas duplicadas.

- Violaciones de estilo: basada en los resultados obtenidos mediante la aplicación de las reglas PMD y se calcula a partir de la siguiente fórmula:

Style = (Errors*10 + Warnings) / ValidLines * 10

Una vez calculadas las métricas ya se puede tener el valor de Quality Index:

QI = 10 – (4.5 * Coding – 2 * Complexity – 2 * Coverage -1.5 * Style)

El plugin además de calcularte el valor global, te indica el valor de las 4 métricas (ejes) llevados a la escala correspondiente en función de su peso en la fórmula del cálculo de la métrica, representando en rojo la proporción que incumple y en verde la proporción que cumple.

Se codifica, se construye la aplicación, pero en demasiadas ocasiones se tiene en cuenta como fin único la entrega del proyecto, dejando al margen o dedicándole menos importancia a una programación pensando en futuros mantenimientos.

La dificultad de los mantenimientos depende de muchos factores y algunos de ellos están por encima en importancia de una codificación clara y de calidad, como por ejemplo la búsqueda de una alta cohesión y un bajo acoplamiento, lo cual no debería ser una excusa para olvidarnos de este asunto ya que no se trata solo de que se pueda comprender lo que se ha programado (que no es poco) sino que un código poco cuidado probablemente influirá en una mayor complejidad ciclomática de la aplicación (además de a la mayoría de las variables que determinan la deuda técnica de la aplicación) dificultando, en consecuencia, a la mantenibilidad.

Generalmente nos solemos acordar de todo esto cuando toca realizar el mantenimiento del sistema de información y nos encontramos con que la forma en que se ha codificado hace que se requiera un mayor esfuerzo y en consecuencia coste. Por este motivo importa y mucho como se codifica.

El problema de todo esto es que resulta complicado detectar este tipo de problemas en tiempo de desarrollo ya que obliga a estar encima y revisar el código, lo que puede hacer que casi cueste más el collar que el perro. Por tanto, tenemos que utilizar otras estrategias que faciliten la localización de estos problemas, como es por ejemplo estudiar variables que se pueden incrementar indirectamente por una mala programación (se puede realizar una revisión periódica de las métricas que devuelve Sonar) y complementarlo, si es posible, con la revisión, también periódica, de una muestra de clases de la aplicación.

Siendo realista, veo complicado que una empresa de desarrollo, salvo que las deficiencias encontradas en el proceso de verificación interno sean serias, se pongan a rehacer a módulos ya codificados, pero por lo menos si se aplican estas políticas se puede reconducir la tendencia en el desarrollo y que el proyecto se encuentre dentro de unos umbrales de calidad aceptables (los cuales en algunos casos podrán ser exigidos y verificados por el cliente y provocar un rechazo en la entrega y la consiguiente necesidad de refactorizar partes de la aplicación lo cual se volverá en contra del proveedor ya que tendrá que sufrir en sus carnes las consecuencias de unas malas prácticas.

Martin Fowler es un importante y reconocido autor especialista en ingeniería del software y en el proceso de desarrollo (particularmente en el campo de la refactorización) el cual ya he tenido la oportunidad de citar en un artículo sobre una regla de CheckStyle que no comprendía su significado.

Steve C. McConnell es otro reputadísimo autor y especialista en ingeniería del software, tanto es así, que durante años fue considerado junto a Linus Torvalds y Bill Gates unas de las personas más influyentes en el negocio del desarrollo de software. En la actualidad posee una empresa llamada Construx Software que se dedica a consultoría y formación sobre buenas prácticas en desarrollo de software.

Ambos autores son responsables de las siguientes citas relacionadas con la comprensión del código:

Martin Fowler (traducción libre): “Cualquiera puede escribir código que un ordenador pueda entender. Los buenos programadores son aquellos que escriben código que los humanos puedan entender”.

Steve McConnell (traducción libre): “El buen código es su mejor documentación. Cuando vayas a escribir un comentario, pregúntate, ¿cómo puedo mejorar el código para que este comentario no sea necesario?”.

Sobre este tema ya publiqué una cita que se atribuye a Martin Golding en unos casos y a John F. Woods en otros que complementa perfectamente a éstas.

Como he comentado en otras ocasiones, hacer las cosas bien, tarde o temprano, marca la diferencia.

Cuando se habla de programación orientada a objetos se dice que un buen diseño es aquel que consigue una alta cohesión y un bajo acoplamiento. Como es lógico, estoy de acuerdo, no obstante de los dos conceptos la cohesión, tal vez por entenderse menos su utilidad, es el hermano pobre, es decir, si se tiene en cuenta alguno de los dos (algo que desgraciadamente no ocurre demasiado, ya que no se suele prestar suficiente atención a la calidad del diseño y de la codificación) se suele echar más en cuenta al acoplamiento, además de ser más inteligible por ser más evidentes los problemas existentes cuando el acoplamiento es alto.

La cohesión mide la relación entre los distintos elementos que componen una clase: atributos y métodos. Una baja cohesión da lugar a las denominadas clases tutti frutti, clases que pretenden hacer muchas cosas y diferentes.

Cuando se tienen clases con baja cohesión implica principalmente:

- Falta de comprensibilidad: La clase no sirve a un aspecto concreto, sino a muchos. Cuando pensamos en un objeto lo hacemos entendiendo que sus partes son necesarias para que exista su todo (es decir, las partes por sí solas tienen poca utilidad y es mediante su relación cuando dan entidad al objeto y hacen que en su conjunto (el todo) valga más que la suma de las partes por separado). Cuando tenemos clases cuyos elementos forman un todo, pero ese todo no es identificable con nada, nos encontraremos probablemente con clases que tienen una baja cohesión, con métodos que no guardarán relación con otros muchos de la misma y que servirán a propósitos distintos. Habrá que tener bien documentada la clase para mantenerla posteriormente, porque si la coge un equipo de desarrollo distinto o ha pasado mucho tiempo desde que se implementó resultará complicado entender para qué sirve y qué utilidad tienen muchos métodos y atributos de la misma.

- Dificulta la mantenibilidad: Además de por la disminución de la comprensibilidad de la clase, al tener más código del que debería tener, se incrementará en consecuencia la complejidad ciclomática de la misma y por tanto será más costoso realizar cambios.

- Dificulta la reutilización: Cuando tenemos una baja cohesión quiere decir que hemos modelado una realidad rara, un híbrido de muchas cosas. Se ha implementado una clase para algo muy específico y lo concreto pocas veces es reutilizable.

- Suelen tener un acoplamiento alto (en comparación a una cohesión baja): Tiene su lógica ya que la clase tendrá más métodos que si los diferentes objetos reales que están dentro de la misma se hubieran encapsulado en sus correspondientes clases. Más métodos implican por regla general llamadas a más métodos de clases y paquetes externos y en consecuencia un mayor acoplamiento.

- Tendentes a sufrir modificaciones: Como estas clases son híbridos de varias la probabilidad de tener que sufrir operaciones de mantenimiento crece (tanto como clases distintas pudieran contener en su interior), lo cual tiene su importancia sobre todo si tenemos en cuenta que este tipo de clases son más complicadas de mantener.

Una forma de medir la cohesión la tenemos con la métrica LCOM4, propuesta a partir de la métrica inicial LCOM1 de Chidamber y Kemerer. Esta métrica forma parte del conjunto de las mismas que calcula Sonar haciendo análisis estático de código.

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 3.209 seguidores