Apr 30

Composición sobre herencia, mi “pequeño” error.

En la programación orientada a objetos hay un principio que dice "preferir la composición sobre la herencia". El motivo tradicional para hacer esta afirmación es que la herencia es una dependencia fuerte entre clases. La clase hija depende fuertemente de la clase padre y la necesita para funcionar. Cualquier cambio en la clase padre puede tener efectos inesperados en cualquiera de las clases hijas y no tenemos, entonces, tanta libertad para cambiar el comportamiento de la clase padre.

Si además trabajamos en proyectos grandes con bastante gente implicada, la pega comentada puede convertirse en fuente grave de problemas. En un grupo grande es casi imposible saber si otra persona se le ha ocurrido heredar de nuestra clase o qué efectos secundarios puede tener si modificamos una clase que necesitamos modificar. Sí, todo esto se evita con un buen diseño, con test automáticos, con gente responsable…. cosas ideales que nunca se dan en proyectos grandes y que sólo se dan en unos pocos proyectos pequeños llevados a cabo por uno, dos o tres programadores buenos.

Otro problema añadido que no se comenta tanto, es la gran dificultad de leer el código cuando hay herencia, cuantos más niveles de herencias, peor. Para saber qué hace una clase, no podemos fijarnos sólo en esa clase, sino en sus clases padre. Y cuando queremos seguir un trozo de código que usa una clase, tampoco podemos fiarnos de esa clase usada, sino que tenemos que asegurarnos muy mucho de qué posibles clases hijas se están instanciando y están realmente siendo usadas ahí. Cuando el código es grande y hay muchos niveles de herencias, casi es imposible seguir el código y se hace imprescindible un buen debugger para seguirlo en tiempo de ejecución. Y arrancar un debugger en un proyecto grande, con varios ejecutables servidor que se comunican entre sí, ejecutables cliente distintos que se comunican con los servidores, puede ser realmente difícil.

Si no está muy justificada, siempre es mucho mejor la composición. Si nuestra clase A necesita la funcionalidad de una clase B, es mejor instanciarla que heredar de ella.

Por supuesto, para no perder la potencia de la orientación a objetos, si prevemos que es necesario, podemos hacer que la clase A use interfaces cuya implementación alguien le pasará desde fuera. De esta forma, la clase A podrá usar en un momento dado una clase B que implemente la interfaz, o una clase C que implemente la interfaz. Eso sí, salvo que esté muy justificado, siempre interfaces (nunca otras clase o clases abstractas) e implementación directa de las interfaces, sin varios niveles de herencias.

Comento todo esto porque hace poco hice una reflexión sobre el código que tenemos heredado de proyectos anteriores. En un momento dado, hace mucho, me dieron un grupo de gente nueva para desarrollar tres proyectos similares entre si, pero para clientes distintos y que debíamos hacer a la vez (plazos de entrega similares).

Yo, con toda mi buena intención, pensé en hacer los proyectos por capas. Las capas más básicas serían aquellas cosas comunes a todos los proyectos y según fuésemos subiendo de nivel, iríamos implementando las cosas específicas de cada proyecto. Y basé estas capas en herencia. El resultado es que una clase específica de un proyecto heredaba de una clase de la capa de abajo que a su vez heredaba de otra clase de la capa de más abajo y así sucesivamente (menos mal que java no permite herencia múltiple).

En esos proyectos todo iba bien y de hecho salieron bien dentro de lo que cabe. El grupo de desarrolladores y yo mismo nos conocíamos bien la estructura de herencias, qué se podía tocar, qué no se podía tocar y dónde había que tocar para incrementar la funcionalidad o cambiar la configuración. Basándose en las capas comunes, el desarrollo fue además bastante rápido.

Pero pasa el tiempo. Ese código se ha seguido usando para otros proyectos (las capas básicas), pero los desarrolladores han ido desapareciendo, cambiando de departamento o empresa, llevando sus propios proyectos … y los nuevos desarrolladores, incluso los más experimentados, se las ven y se las desean para ver dónde demonios se hace una cosa. Empiezan por la clase del proyecto y tienen que ir bajando por el árbol de herencias hasta llegar a clase padres que son realmente complejas. ¿Por qué son complejas? Porque tienen que ser lo suficientemente versátiles como para soportar a todos las clases hijas de todos los proyectos pasados y actuales. Esas clases abusan de tipos "Object", de Hashtables que no tienen definidas las posibles claves en ningún sitio, de paneles swing que no tienen nada porque se construyen sobre la marcha en tiempo de ejecución, etc, etc.

Resumiendo, un código que nos ayudó mucho en los tres primeros proyectos, pero que se fue convirtiendo en una pesadilla cuando los desarrolladores originales dejaron de estar disponibles y a los pocos que quedamos nos da "yuyu" de mantener, porque incluso a nosotros mismos nos resulta complejo con el paso del tiempo. Y eso sólo por las herencias, aparte de la posible complejidad de la algorítmica implicada.

May 07

Mirando OSGi

osgi allianceOSGI es una de esas cosas de las que quería enterarme de qué iba y aprovechando el manual de OSGi de Roberto Montero publicado en javahispano, me he puesto a ello.

El manual, para alguien como yo que tiene ciertos conocimientos de java y no le suenan a chino cosas como fichero de manifiesto, classloader o maven, pero que no tiene ni puñetera idea de qué es OSGi, está muy bien. Da una explicación desde el principio y mostrando un ejemplo "Hola Mundo" en diversas plataformas y con distintas herramientas (Equinox, Apache Félix, Eclipse, Pax Constructor, etc). Al final me queda más o menos claro qué es OSGi y sólo queda hacerme mis propios ejemplos y pruebas.

La idea básica de OSGi es que es una forma de desarrollar aplicaciones java a base de hacer módulos más o menos independientes, pero que pueden colaborar entre ellos. Estos módulos (llamados bundles) no son más que jars con algunas clases especiales propias de OSGi y un fichero de manifiesto con campos especiales, que entiende OSGi. Los bundles pueden colaborar entre ellos, indicando qué paquetes java exportan a otros módulos y qué paquetes java de otros módulos necesitan. Una plataforma que implemente OSGi (Equinox o Apache Félix entre otras) es capaz de cargar, arrancar o parar estos módulos en tiempo de ejecución, sin necesidad de tirar la aplicación y volver a arrancarla. También es capaz de saber qué módulos dependen de cuales para pararlos si les faltan dependencias o arrancarlos sólo cuando todas sus dependencias están cargadas.

Sin embargo hay un problema que tengo de hace mucho y que veo que OSGi no me va a solucionar. Cuando hay dos módulos (jars) que se quiere que colaboren, al final no queda más remedio que uno de ellos vea (necesite) al otro para compilar, ya que si un módulo usa otro, debe ver al menos alguna interface o tipo de dato. Esto hace que los módulos no sean realmente independientes y no puedas aprovechar un módulo para otro proyecto. Si en otro proyecto quiero llevarme el móudlo A y este tira de una interface de B, pues me tengo que llevar B también y éste, desgraciadamente, en una de sus clases que no necesito para nada, tira de una interface del módulo C y así sucesivamente. Para proyectos grandes con muchos programadores (expertos y novatos), lo normal es que todos los módulos acaben dependiendo de todos e incluso empiece a haber dependencias cruzadas. Sí, ya sé que "sólo" es cuestión de pensar antes que módulos debe haber, cómo dependen unos de otros y "sólo" hay que conseguir que todos los programadores respeten esa arquitectura.

Una posible solución, que había descartado, para este problema es que cada módulo fuera en realidad dos módulos. Uno pequeño, con sólo las interfaces y clase de datos (beans) que es lo que dependerían los demás módulos. Y otro módulo más grande con todo el resto del código. Lo descarté porque en un proyecto grande con 30 o 40 módulos, se nos duplica el número de módulos y de jars.

Sin embargo, en los ejemplos OSGi que he visto, parece que hace eso mismo. Si crea un módulo que es un servicio que van a ver otros módulos, crea dos módulos: uno con la interface del servicio y los bean de datos, el otro con la implementación. Los módulos que usen servicios de éste, sólo dependen del módulo pequeño con la interface.

En fin, a jugar un poco con OGSi y a repensar esto de partir los módulos en dos submódulos: lo visible al exterior y la implementación concreta.

Jun 17

El difícil compromiso entre KISS y OO

 

KISS es la abreviatura de "Keep it simple, stupid" o traducido, "Manténlo simple, estúpido". La idea es que el código debe hacerse siempre lo más sencillo posible. Esto ayuda a que haya menos errores y a que sea más fácilmente mantenible en un futuro, ya que será más fácil entenderlo por terceras personas y corregir posibles errores que queden.

OO es la orientación a objetos. Un código diseñado con orientación a objetos debe usar cosas como clases, herencias, polimorfismos, encapsulación, etc. Tras muchos años de diseño orientado a objetos, se ha llegado a que hay determinadas formas de combinar clases e interfaces para obtener unos resultados óptimos, los llamados patrones de diseño. Como consecuencia, un buen diseño orientado a objetos normalmente implica el uso de determinados patrones de diseño. También, en un buen diseño orientado a objetos, se usan varias clases cada una de ellas con una responsabilidad concreta muy definida, es decir, suelen ser clases pequeñas y que sólo hacen cosas concretas. El fin último de la orientación a objetos es conseguir que el código sea reutilizable. Podemos llevarnos a otros proyectos sólo aquellas clases que realmente necesitamos y además, están hechas de tal manera, que no es necesario modificarlas, sino que nos sirven tal cual.

Pues bien, KISS y OO, hasta cierto punto son opuestos y veamos un pequeño ejemplo tonto. Imagina que vamos a hacer un programa que pida al usuario por pantalla los coeficientes de una ecuación de segundo grado y nos muestre el resultado por pantalla.

Si llevamos KISS a sus últimas consecuencias y tratamos de hacer el código lo más simple posible, sin preocuparnos en absoluto de posible reusabilidad de lo que hacemos, lo más sencillo es hacer una única clase con un método main(). En ese método pedimos por pantalla los coeficientes (podemos hacer un método estático encargado de pedir un número por pantalla), echamos las cuentas (podemos meterlas en otro método estático) y sacamos los resultados (si acaso en otro método estático). Resultado final: una clase sencilla, que hace lo que tiene que hacer, con quizás tres métodos además del main(), pero que no es en absoluto reusable. Hace lo que hace y nada más. Sólo pide coeficientes por pantalla, echa unas cuentas y saca los resultados por pantalla.

Si pensamos más bien en hacer algo reusable y aplicamos orientación a objetos, posiblemente hagamos una clase EcuacionSegundoGrado a la que le pasemos los coeficientes y nos devuelva los resultados. Haremos otra clase (o dos clases) para pedir y sacar cosas por pantalla. Estas clases, idealmente, deberían implementar interfaces de pideDato() y muestraDato(), de forma que si más adelante en vez de pantalla es una base de datos, nos baste con implementar esas interfaces. Finalmente, posiblemente hagamos otra clase más con un main() encargada de instanciar las anteriores y hacerlas funcionar conjuntamente. El resultado final es que tenemos una serie de clases con responsabilidades separadas y reusables. Podemos coger cualquiera de esas clases y llevarla tal cual a otro proyecto y usarla a nuestro gusto.

Pero…. hemos perdido la sencillez. Ojo, no quiero decir que estas clases no estén bien hechas o que sean dfiíciles de entender si están bien hechas, pero donde antes teníamos una única clase y siguiendo el main() lo veíamos todo, ahora tenemos tres o cuatro clases, dos interfaces y seguir el código nos obliga a navegar de una a otra … y navegar a través de una interface no es sencillo, puesto que no sabemos qué implementación hay detrás (en este ejemplo sí sabemos cual hay detrás, porque sólo hay una). Obviamente, todo esto es peor si nuestro programa no es tan tonto como una ecuación de segundo grado.

¿Cual es entonces la situación ideal?. Las metodologías ágiles, en general, nos dan la solución bien clara. Haz el código lo más simple posible sólo para lo que tienes que hacer ahora (la historia de usuario concreta que estás haciendo ahora, el bug que estás corrigiendo, etc).

Si tenemos una historia de usuario que dice que pedimos los coeficientes por pantalla, resolvemos la ecuación y mostramos por pantalla los resultados, debemos, sin dudarlo, hacer la primera implementación, la del KISS puro y duro, tal cual nos aconsejan las metodologías ágiles. Cualquier metodología ágil nos aconseja también a hacer test automáticos de prueba. de forma que si más adelante tenemos que rehacer, tengamos seguridad de no estropear.

Supongamos ahora una segunda historia de usuario que nos dice que los coeficientes se deben leer de base de datos. Bien, este y no otro es el momento de hacer la interface con el método pideCoeficiente(), hacer dos implementaciones, la de pantalla y la de bd, y cambiar nuestro código para que no vaya directamente contra pantalla, sino contra esta interface cuya implementación alguien le pasará de fuera. Al tener test de la primera parte, tendremos seguridad de no haber estropeado lo que ya funcionaba después de tocar y ya habremos empezado nuestro diseño OO, por necesidad, y no por gusto. Ojo, todavía no hemos extraido la clase EcuacionSegundoGrado ni hemos hecho metodo en la interface de muestraResultado(), porque nadie nos ha pedido mostrarlo en otro sitio que no sea pantalla.

Resumiendo, es muy importante para tener un código lo más claro posible, no lanzarse sin ton ni son a hacer clases, interfaces, aplicar patrones y usar a tope la OO. Hay que ser realista y usar esas características de OO sólo cuando es estrictamente necesario o cuando estamos seguros al 99% de que vamos a necesitarlas. En el ejemplo anterior, si tenemos la lista de historias de usuario en algún sitio y vemos que la segunda está justo detrás de la primera, quizás, y en contra de las metodologías ágiles estrictas, podamos ir haciendo la interface ya en la primera implementación.

Apr 28

Inyección de dependencia vs Localizador de servicios

 

Supongamos que estamos haciendo un pequeño proyecto, la típica agenda de contactos. Para simplificar, digamos que la agenda sólo consta de una clase Agenda y de una clase BaseDatos para almacenar los contactos en la base de datos. Agenda, en su código, hace un new de BaseDatos y lo utiliza cada vez que quiere leer, insertar o modificar la base de datos.

Esta primera aproximación, la de hacer el new de BaseDatos puede ser suficiente para una aplicación sencilla en la que nadie nos vaya a cambiar nada. Pero supongamos que estamos en un proyecto más grande, en el que pueden en un momento dado decirnos "no uses base de datos, guarda todo en fichero" o "no uses base de datos directamente, envía todo por socket a un servidor remoto". O simplemente viene un amiguete y me dice "tu clase Agenda me vale tal cual, pero yo no tengo base de datos, sino que llamo a un Servlet remoto de una web". En este caso lo del new de BaseDatos directamente en la clase Agenda no es tan buena idea. Para estas nuevas aplicaciones, debemos modificar el código de la clase BaseDatos para adaptarse a las nuevas necesidades, o bien tocar la clase Agenda para que haga un new de SocketRemoto, o de Fichero, o de ServletRemoto.

De todos es conocida la solución a este problema. Basta hacer una interface IfzBaseDatos con los métodos que necesita la clase Agenda y no hacer que Agenda haga el new de ninguna clase. De alguna forma, Agenda recibirá o conseguirá una instancia de una implementación concreta de IfzBaseDatos y la usará. Esto permite que para las nuevas necesidades que surjan podamos sólamente hacer nuevas implementaciones de IfzBaseDatos y hacérselas accesibles a Agenda. A nuestro amiguete podremos pasarle la clase Agenda y que se haga el la implementación de IfzBaseDatos a su gusto.

¿Cómo pasamos una implementación de IfzBaseDatos a Agenda?. Hay principalmente dos aproximaciones:

  1. Inyección de dependencia: La clase Agenda debe tener un método setBaseDatos(IfzBaseDatos) o un constructor que reciba un parámetro IfzBaseDatos. De esta forma, el que haga new de Agenda hace también new de una implementación concreta de IfzBaseDatos y se la pasa a Agenda.
  2. Localizador de Servicios: Hacemos una clase LocalizadorServicios que puede tener un método estático IfzBaseDatos getBaseDatos(). Este método estático hará new de la implementación concreta de IfzBaseDatos y la devolverá. Agenda, cuando necesite IfzBaseDatos, se la pedirá al LocalizadorServicios. Pero, de esta forma, Agenda ve directamente a LocalizadorServicios y si LocalizadorServicios hace directamente el new de una clase BaseDatos, de forma indirecta Agenda necesita BaseDatos, por lo que no hemos solucionado el problema, nuestro amiguete seguirá necesitando una base de datos. Necesitamos complicar un poco LocalizadorServicios para que no haga directamente el new de BaseDatos. Por ejemplo, LocalizadorServicios podría tener un método estático setBaseDatos(IfzBaseDatos) y el método getBaseDatos() devolver lo que se le ha pasado en el método setBaseDatos(), o bien se le puede pasar el nombre de la clase que debe instanciar, para que lo haga con un Class.forName("BaseDatos").newInstance() o algo parecido.

¿Qué solución es mejor?. En principio, a la vista de las dos opciones tal cual se han comentado, parece mucho mejor solución la de inyección de dependencia. La opción de Localizador de Servicios parece más enrevesada para finalmente hacer lo mismo que habíamos hecho en la primera opción: un new BaseDatos y pasárselo a una clase LocalizadorServicios en vez de a una Agenda, o tener que usar algo tan raro como en Class.forName().newInstance(). Sin embargo, esta segunda opción puede ser mejor en algunos casos.

Imagina que nuestra Agenda ya no es tan simple y que nuestra clase Agenda está compuesta por varias clases, ventanas, paneles, formularios, botones, JTables y muchas más cosas. Imagina, por ejemplo, que entre los paneles y formularios hay un campo que es para pedir la fecha de nacimiento e imagina que hemos hecho una clase EditorFecha que hereda de JFormattedTextField y que es estupenda para las fechas. Pero imagina que también nos intuimos que más adelante nos van a decir que ese editor no les gusta y que quieren usar un JCalendar. Bueno, es un caso similar al mencionado anteriormente, Agenda necesita una clase que puede cambiar. Podemos hacer una IfzEditorFecha con los métodos que necesitemos y construir nuestros paneles usando ese IfzEditorFecha. Pero, ¿qué pasa si en total necesitamos diez editores de estos repartidos por nuestra aplicación?. Si usamos la opción de inyección de dependencia, deberemos poner diez métodos setEditorFechaNacimiento(IfzEditorFecha), setEditorFechaGraduacion(IfzEditorFecha), etc, etc a nuestra clase Agenda. Es más, necesitamos que todas las clases de nuestra aplicación Agenda tengan este tipo de métodos, para pasarse el editor desde la clase principal de Agenda hasta el formulario concreto que lo usa. En este caso concreto, vemos que es muy incómodo el procedimiento de inyección de dependencia. Con un LocalizadorServicios que tenga un IfzEditorFecha getEditorFecha(), cada formulario concreto podría llamar directamente a LocalizadorFecha para obtener un nuevo editor. Es más, podemos ir añadiendo funcionalidad a nuestra agenda añadiendo más formularios que necesiten este tipo de editores sobre la marcha, sin necesidad de añadir otra ristra de métodos set() para un nuevo campo en un nuevo formulario.

Resumiendo:

  • la inyección de dependencia es en principio mejor y más sencilla para las clases de alto nivel, de las que sólo debemos pasar una a la clase principal (Agenda) y que no van a tener que bajar hasta clases de muy bajo nivel (hasta el último formulario del último panel del último botón), como por ejemplo, la IfzBaseDatos.
  • El Localizador de servicios es mejor para clases de bajo nivel, de las que nuestra aplicación puede necesitar varias y en distintos sitios, evitándonos así llamadas setEditorFecha() encadenadas desde las clases de más alto nivel (Agenda) a las de más bajo nivel (FormularioTerciarioDeLaVentanaSecundaria).

Como siempre, el punto concreto en el que una cosa deja de ser buena para ser mejor la otra, depende de gustos personales y facilidad de programación para nuestro caso concreto.

Oct 11

Sobreorientación a Objetos

 

Ando últimamente un poco preocupado sobre un tema.

Según puedo comprobar en la realidad, el 90% de los programadores no sabe realmente de orientación a objetos. Sí, el 90 % de ellos a lo mejor han usado alguna vez, por ejemplo, un patrón observador: un ActionListener de java lo es y sólo por hacer un botón que haga algo, ya el 90% de los programadores lo han usado. Implementar uno en tu diseño/programa también. El patrón observador es sencillo y bastantes programadores lo han implementado alguna vez en sus proyectos, incluso muchas veces.

Pero ¿qué pasa con el resto de patrones? ¿Quien conoce el patrón estado, o el patrón comando, o cualquier otro de esos que hay por ahí menos conocidos? ¿Y cuántos lo han implementado alguna vez para algo? ¿y cuántos de esos poco lo han hecho correctamente?. Ya estamos llegando al otro 10 % de programadores, los que realmente entienden la programación orientada a objetos, los que realmente entienden la utilidad de esos patrones y los que realmente son capaces de usarlos y sacarles partido en proyectos reales, los que después de pasarse ocho horas programando en el trabajo, llegan a casa, encienden el ordenador y se ponen a programar lo que les gusta.

Pero ahí está el problema. Para ese 10% de programadores el código que han hecho es entendible, fácilmente modificable y adaptable y una pequeña maravilla de la técnica. ¿Y qué pasa cuando tiene que mantener ese código un programador del otro 90%, que suele ser el 90% de las veces?. Pues que se encuentran un código que no entienden, en el que no saben qué buscar ni donde, en el que no tienen ni idea de qué tocar y en el que toquen lo que toquen, fastidian algo.

Y esa es mi preocupación. ¿Hasta qué punto es entonces bueno usar la orientación a objetos?. La situación real es que cuando necesitas modificar/adaptar un sofware ya hecho con intención de reutilizarlo en otro proyecto, depurar algún bug, añadirle algo de funcionalidad, etc, el programador original ya está en otras cosas y sólo tienes un 10% de posibilidades de que la persona a la que pongas a hacerlo pueda hacerlo.

Al final, el código hecho por ese 90% de programadores que hace un uso básico de la orientación a objetos es realmente el código que resulta mantenible, el código que cualquier programador puede entender y cualquier programador puede tocar con cierta seguridad, aunque no sea todo lo elegante ni correcto que debiera ser.

Dicho de otra forma, muchos son capaces de tocar un texto en prosa para que diga otra cosa. Muy pocos son capaces de entender una poesía genial, modificarla para que diga otra cosa, y siga siendo genial.

En fin, cada vez soy más partidario de hacer código más "lineal", usando la orientación a objetos lo más simple posible y en los sitios que realmente se le saque un partido real. Si estás en un proyecto en el que todos los programadores son programadores de "élite", en el que todos ellos realmente viven la orientación a objetos y todos ellos de coeficiente intelectual alto, adelante, usa todos los patrones que quieras. Si estás en un proyecto normal con programadores normales (el 90% de las veces y el 90% de los programadores), piénsatelo dos veces antes de poner un patrón estado.

Jul 23

Pensamientos sobre proyectos grandes y Spring Framework

En su día, para nuestros proyectos grandes, teníamos muchas librerías y módulos separados. Cada programador solía ser responsable de uno de los módulos y tenía su proyecto independiente, con sus programas de prueba, simuladores y todo lo que le hiciera falta.

Eso estaba bien, pero tenía una ligera pega. No usábamos algo como maven y no teníamos control ninguno de dependencias cruzadas. Cada programador ponía sus dependencias de otros módulos según lo necesitaba. El problema, al final, es que no había forma de compilar eso desde cero, ya que era fácil que un módulo necesitara del jar de otro módulo, que a su vez necesitaba de un tercero y tras una cadena más o menos larga, había un módulo que necesitaba del jar del que estamos compilando. Sí, ya sé que es una falta grave de diseño y debería haberse pensado al principio y dejar claras esas dependencias, pero también es cierto que entre lo que lo pensado antes y la realidad después hay muchas diferencias.

Al usar maven, decidimos hacer un proyecto grande con subproyectos debajo (uno por módulo). Así maven comprueba las dependencias y si un programador pone una dependencia que no debe, maven le protesta. Sin embargo, esto también tiene una gran pega. El compilado desde cero se hace desde arriba del todo y el fallo de un módulo hace que los demás ni siquiera intenten compilar. Además, la dependencia se hace entre fuentes, por lo que para estar a la última, necesitas hacer update de todos los fuentes. Eso, en un proyecto grande, puede ser un buen rato casi todos los días.

Al descubrir que se puede usar el core de Spring Framework, aunque sean aplicaciones de escritorio, para instanciar los módulos y usar los eventos de Spring Framework para comunicarlos entre sí, me da la impresión de que se puede hacer relativamente fácil que los módulos hablen entre ellos y se pasen datos sin necesidad de que tengan dependencias entre ellos. Los primeros mini-proyectos en la que hemos usado este framework y hemos hecho módulos indpendientes totalmente, comunicados a través del framework y de código de transformación de tipos de datos de un módulo a los tipos del otro en el main, están resultando una pequeña maravilla en cuanto a organización.

Así que me estoy planteando el volver a módulos separados, en proyectos independientes, y con prohibición de poner dependencias de otros módulos, salvo algunos muy concretos pensados más como librerías comunes que como módulos a instanciar como beans de Spring Framework.

La pega es la de siempre, la paliza de mover todos los repositorios de CVS para que tengan otra estructura y el retocar todo el código, sobre todo quitando dependencias de unos módulos con otros. Todo se andará, poco a poco.

Una pequeña aclaración sobre las dependencias entre módulos. Suponamos que tenemos modulo1.jar y modulo2.jar y que modulo1.jar necesita llamar a cosas de modulo2.jar. Para no meter la dependencia a lo bestia, normalmente hacemos una InterfaceModulo2 con las llamadas a las cosas del módulo2 que sean de interés desde fuera. Por supuesto, esa interface está en modulo2.jar. Y por supuesto, los parámetros y retornos de los métodos de la interface son tipos primitivos de java o bien tipos que están en modulo2.jar. Pues bien, eso hace que modulo1 dependa de modulo2, ya que ve al menos a la interface y a los tipos de parámetros y retornos. Por supuesto, una opción es hacer un tercer módulo InterfaceModulo2.jar con la interface y los tipos, pero me parece multiplicar el número de jars a lo tonto.

La opción que estamos planteando ahora es que modulo1 no vea en absoluto a modulo2. Cuando necesite algo de módulo2 (en realidad, cuando necesite algo, lo que sea), debe lanzar un "evento" indicándolo. Es el código del main el encargado de recoger ese evento y hacer la petición a modulo2, para pasar luego los resultados a módulo1. Esto puede parecer costoso, sobre todo si hay mucha necesidad de cosas de modulo2 por parte de modulo1, pero… si hay tanta necesidad de cosas ¿no estará mal pensado hacer dos modulos? ¿no estará mal pensada la funcionalidad de cada módulo?.

En fin, un pequeño rollo, pero como indico en el título, son símplemente "pensamientos".

Apr 05

¿Planificación de proyectos?

Cada vez estoy más escéptico con el tema de la planificación de proyectos. No me refiero a hacerla, revisarla periódicamente, rehacerla, que es "relativamente sencillo" (ni de coña, hace falta mucha disciplina y experiencia). Me refiero a hacer una planificación que luego se cumpla.

Quizás gente con experiencia en planificaciones puede planificar más o menos correctamente un proyecto que dure unos meses y con unos pocos programadores (dos, tres, quizás cuatro). Pero, en nuestro caso, estamos hablando de varios proyectos con funcionalidades similares, pero lo suficientemente distintas como para requerir modificaciones, todos en paralelo, con alrededor de treinta programadores en total y plazos de entrega de uno a tres años.

Cualquier cosa que leas de planificación que te dice que hagas tareas muy granulares, del orden de tiempo estimado de uno o dos días por tarea. Si echamos unas cuentas, treinta programadores durante dos años, con tareas de 1 día, son aproximadamente …. ¡¡Buff, ni me atrevo a calcularlo!!.

Obviamente, no se puede calcular a priori todas esas tareas. La solución supuestamente es fijar pequeñas entregas cada uno o dos meses, dividir a la gente en grupos y planificar las tareas para esos dos o tres meses. Estupendo. ¿Y el resto del proyecto? ¿Y las demás entregas?. Por supuesto, todas ellas están fatalmente planificadas, porque ni se han detallado lo suficiente, ni se tiene muy claro que es lo que se tiene que hacer. ¿De qué sirve tener perfectamente planificado el primer mes o reajustar la planificación del primer mes si no se sabe nada fiable del resto del proyecto?

Al final da la impresión de que lo que hay que hacer es tener claro qué cosas son importantes y dedicarse a ellas primero, dejando lo secundario para el final por si da tiempo. Al final es algo así como "estará lo que dé tiempo a hacer en plazo y el resto no se hace", por lo que efectivamente es importante hacer lo más importante primero para que al menos eso esté. Pero ninguna planificación nos dirá a priori cuántas de esas cosas van a estar hasta que estemos ya muy cerca del final.

Quizás hay que dividir el plazo total entre las funcionalidades a implementar y hacer entregas cada uno o dos meses con esas funcionalidades. Pero para que se cumpla el plazo total, va a haber que pasado el tiempo de la primera entrega, dejar las funcionalidades que entren en ella como estén y pasar al segundo grupo. Por supuesto, en cada momento habrá que decidir si se continúa con las que no se han acabado (a costa de quitar otras) o se dejan como están para seguir con las siguientes. En cualquier caso, NO es una planificación para que se cumpla, sino es más que nada saber cómo se va para asumir el retraso y quitar cosas.

Cada vez estoy más decepcionado con este tipo de cosas.

Feb 02

Referencia rápida de patrones de diseño

Veo en Arragonán, que a su vez viene de mcdonaldland, una referencia rápida de los patrones de diseño. Un jpg/pdf en el que tenemos un pequeño esquema y resumen de los patrones de diseño GOF.

patrones de diseño

Dec 21

Constructores vs Setters

Lo que cuento aquí está extraido del artículo de Martin Fowler de Inversión de Control, pero como es interesante, pues eso, lo cuento aquí.

La cuestión, a la hora de inicializar una clase, es si lo hacemos pasándole todo lo necesario a través del constructor o bien por medio de métodos set().

En principio, es mucho más claro usar los constructores. En un constructor está claro qué debemos pasar a esa clase para que funcione correctamente y desde el mismo momento que la instanciamos, esa clase está en condiciones de funcionar, ya que  tiene todo lo necesario. Con los método set() no tenemos esa facilidad. Puede haber métodos set() que no sean necesarios para el correcto funcionamiento de la clase, por lo que no tendríamos necesidad de llamarlos. Y al revés, un método set() obligado se nos puede olvidar y la clase no funcionaría. Además, si no conocemos la clase, no podemos saber con los métodos set() cuales son obligatorio y cuales no, mientras que en el constructor está claro.

Por todo ello, en principio es mejor usar constructores. Pero hay una pega.

A veces hay muchas configuraciones posibles para la clase, según qué parámetros pasemos. Eso obligaría a hacer varios constructores. Si además, usamos herencia, es posible que en nuestra clase, además de nuestros constructores, tengamos que redefinir los de la clase padre, por lo que el número de constructores puede llegar a ser abrumador. Incluso a veces, al llamar al constructor de la clase padre, es posible que no podamos construir sobre la marcha uno de sus parámetros.

En ese caso, posiblemente sea mejor usar los métodos set(). Y como propone Martin Fowler, quizás sea buena idea llamar initParametro() a los que son obligatorios o a los que sólo se puede llamar una vez, aunque rompamos la regla de los beans de java.

Por mi parte, siempre había tenido esta duda y me había tropezado con los dos problemas -no saber que métodos set() son obligatorios o tener que hacer varios miles de constructores-. Todo esto me ha parecido buena idea, -usar constructores si es posible o métodos init() para los obligatorios- así que trataré de aplicarla a partir de ahora.

Dec 20

¿Se puede hacer realmente diseño antes de codificar?

Ando últimamente un poco preocupado. Supuestamente debo dedicarme a diseñar código y otros lo codificarán. El problema principal que veo es que hay mucha distancia desde el punto de vista teórico del diseño y "las trincheras" de la codificación.

Me explico.

Cuando haces diseño, sobre todo de grandes aplicaciones, no puedes meterte en los detalles del código. Puedes pensar una arquitectura, unos módulos, las responsabilidades de los modulos y las interfaces de los mismos. La implementación de dichas interfaces no puedes meterte en ellas, ya que ni eres capaz de prever a priori todo lo que hay que hacer, ni sabes los problemas de codificación que van a surgir.

Ahora llevo unos días codificando sobre código ya hecho y más o menos pensado por mi, pero codificado por otros. Estoy haciendo código nuevo aprovechando ese código ya existente. Y es complicado. A pesar de que más o menos había ideado yo la esctructura de todo eso, y más o menos la sigue, veo que los problemas que se encontraron al codificarlo los han ido resolviendo como han podido, unos mejor que otros. Veo que ese código, que teóricamente debería poder reutilizar tal cual, no me vale tal cual. Tengo que hacer cambios -no muy grandes, pero sí cambios-. La culpa es en parte mía, porque mi diseño inicial era válido para los proyectos que había en curso, pero no lo bastante como para estos nuevos. También es en parte culpa de los codificadores. Posiblemente movidos por la prisa que siempre les meten los jefes, muchas veces optaron por las soluciones rápidas en vez de las buenas.

Sobre todo veo código repetido, cosas que deberían ser comunes, que yo no había previsto como comunes y que los programadores, movidos por las prisas, acaban rápidamente con copy-paste. Y ahora me veo obligado a hacer yo también otro copy-paste de ese trozo para el nuevo proyecto -nuevamente movido por las prisas- o bien "refactorizar" -ignorando las prisas-. Tocar el código hecho, llevarme esas copias a un sitio común y probar que todo sigue funcionando -los test unitarios brillan por su ausencia-.

Todo esto me lleva a pensar que es muy difícil conseguir un diseño bueno a priori y que es realmente difícil conseguir que se siga. El diseñador debe estar muy metido en el día a día del código para ver lo que se está haciendo, lo cual hace imposible que pueda abarcar aplicaciones demasiado grandes. Metiéndose en el día a día, puede controlar el diseño de trozos relativamente pequeños.

Cada vez estoy más convencido de que las estructuras jerárquicas, en que uno hace arquitectura, otros hacen diseño detallado y otros codifican no llevan realmente a ningún sitio. Es mucho mejor un grupo de gente con mucha comunicación, que hagan un diseño previo y, sobre todo, que todos ellos sean programadores hábiles, a los que les guste programar y tengan muchísimo interés en hacer el código bien… ¡¡ y que no les metan prisa !!.