Inversión de Control: Mi propio resumen
Un pequeño resumen del original Inversión de Control.
Supongamos que tenemos una clase LectorDeFichero que lee de un fichero en un formato concreto una lista de películas de video con sus datos asociados -director, actores, etc- y es capaz de devolverte esa lista. Supongamos ahora una clase BuscadoraDePeliculas a la que le damos el nombre de un actor, director o lo que sea y es capaz de buscarla, usando LectorDeFichero para obtener la lista total de películas y buscar en ella.
Es bastante normal hacer que la clase BuscadorDePeliculas haga un new de LectorDeFichero y luego la use. Esto, si es sólo para nuestra aplicación es perfectamente válido. Pero ¿Qué pasa si queremos pasarle nuestra clase BuscadorDePeliculas a un compañero que tiene almacenados los videos en una base de datos en vez de en un fichero?. ¿Y si lo tiene también en un fichero, pero con un formato distinto?. Pues que nuestra clase no le vale tal cual. Debe coger los fuentes de LectorDeFichero y modificarlos.
El patrón de inversión de control nos indica una forma mejor de hacer esto. Consiste básicamente en hacer que la clase BuscadorDePeliculas no sea la que decida que tiene que usar la clase LectorDeFichero, sino que algún trozo de código externo se encargará de pasarle a BuscadorDePeliculas la clase LectorDeFichero. Por eso se conoce como inversión de control: BuscadorDePeliculas deja de decidir qué es lo que va a usar y alguien se lo pasa.
Por supuesto, para que BuscadorDePeliculas sea realmente reutilizable cuando las películas están en ficheros con otro formato o en base de datos, BuscadorDePeliculas debería admitir una interface InterfaceLectorDePeliculas, con un método List<Pelicula> getPeliculas().
Al mecanismo de pasarle a BuscadorDePeliculas el lector que deseemos se le llama inyección de dependencia. Se puede hacer de varias maneras. Una es pasarle la InterfaceLectorDePeliculas en el constructor. Otra opción es pasársela en un método setLectorDePeliculas().
Existen varios frameworks, como Spring o PicoContainer que realizan los news de las clases y la inyección de dependencias de forma más o menos automática. Bien por código, bien por un fichero de configuración, decimos a estos frameworks que queremos instanciar las clases BuscadorDePeliculas y LectorDeFichero y que a BuscadorDePeliculas hay que pasarle el LectorDeFichero. El framework se encarga, a partir de ese fichero de configuración, de instanciar ambas clases y de pasarle la una a la otra. Este tipo de frameworks se conocen como contenedores de inversión de control o de inyección de dependencias.
En cuanto a si es mejor inyectar dependencias por medio del constructor o de métodos set(), suele ser preferible usar el constructor. Un constructor deja más claro al programador qué cosas se deben pasar a esa clase para que funcione y desde el mismo momento de la instanciación esa clase está lista para funcionar. Los métodos set() corren el peligro de que se nos olviden y tampoco dejan muy claros cuales son obligatorios y cuales opcionales. Una opción sería llamar a los obligatorios initParametro() en vez de setParametro(), con lo que dicha pega quedaría más o menos subsanada.
Sin embargo, los constructores presentan pegas, sobre todo si usamos herencias. Muchas veces es necesario hacer varios constructores y si usamos herencia, debemos implementar todos los de la clase padre más los propios nuestros. Esto puede dar lugar a un crecimiento desmesurado del número de constructores a implementar. En estos casos, por comodidad, empieza a ser más aconsejable el uso de métodos set() -o init() si seguimos la nomenclatura-.