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.

Entradas relacionadas:

  • No hay entradas relacionadas.

2 Responses to “Inyección de dependencia vs Localizador de servicios”

  1. serfer2 Says:

    Me encanta leer las entradas de tu blog.
    Además explicas las cosas con mucha sencilez.
    Felicidades 😉

  2. el guardian Says:

    Me parece interesante tu entrada. Me gustaría añadir un par de apuntes.
    Para la inyección de dependencia el rol de “assembler” en java se hace a través del
    API java.lang.instrument (java 5)
    Para el “ServiceLocator” en (Java 6) se añadió directamente en el “core” de java una implementación de ese patrón que se plasma en la clase ServiceLoader la cual hace innecesario usar “reflection”
    En otro orden de cosas considero que la inyección de código y la localización de servicios no tienen por que ser consideradas una disyuntiva; un ejemplo de esto son los ejb3 (hace uso de ambos patrones)

Leave a Reply