viernes, 22 de febrero de 2008

Dependency Injection

Al parecer este patrón de diseño DI resuelve el problema de acoplamiento que sufren las aplicaciones que impiden la extensibilidad, mantenibilidad y la prueba (o testing ) y esta muy de moda. Es el patrón que nos llevará a otro nivel en las aplicaciones desacopladas, permitiendo la extensibilidad que se traducirá en productividad y alta calidad del software, algo que hemos esperado por años y que hoy en día esta madurando y convirtiéndose en una realidad en la industria de software y en la comunidad de desarrolladores en todo el mundo.
Esto no es nuevo, y viene del mundo java (su mayor expositor el chief scientiest y gurú de los patrones de diseño Martin Fowler
http://www.martinfowler.com ), pero Microsoft ha hecho para la comunidad de usuarios de desarrollo de la plataforma .NET una solución muy elegante. Y fue estrenada con las Software Factories, en especial la Web Client Software Factory, que a través de un agente llamado Object Builder de las Microsoft Enterprise Library 3.1 inyecta de manera elegante por medio de atributos que encuentra en las diferentes implementaciones dentro de las WCSF los servicios o componentes dependientes de la clase a crear en memoria. Esto tampoco quiere decir que Microsoft hasta ahora lo esta implementando o lo conoce. Hay artículos del 2005 que hablan ya de esta práctica en la aplicación de este patrón.
Es importante mencionar que el DI no es el único patrón de diseño que soluciona el problema de aplicaciones fuertemente acopladas esta el observer pattern entre otros, que también se utiliza para desacoplar segmentos de código que estaban fuertemente acoplados. Lo interesante de DI es que resuelve un sin número de problemas con distintas implementaciones. También cabe mencionar que Microsoft no es el único que ha hecho una implementación de el patrón DI entre ellos están Casttle Project
http://www.castleproject.org/ y Spring Net http://www.springframework.net/ . No obstante la gente de Patterns&Practices ha visto tanto interes en ello que ha creado un nuevo proyecto llamado Unity el cual ahislo de la composite web application block el framework de DI (para los intresados en el proyecto: http://www.codeplex.com/unity ).
Es importante mencionar desde el punto de vista de diseño de patrones que el DI cumple creo yo con dos importantes principios de diseño y son los siguientes:
Favor Composition over inheritance and
Strive for loosely coupled designs between objects that interact.
El primero muchas veces la solución se encuentra en la colaboración de clases a través de interfaces y en la descomposición de una clase en varias y no en la herencia misma que tiene sus pro y contra de esto los patrones de diseño para mas información el libro de Head First Design Patterns es una guía completa de patrones de diseño y un excelente libro para comprenderlos.
El segundo principio que muchos patrones de diseño cumplen se base en las siguientes premisas:
La única cosa que sabe la clase consumidora acerca de la clase proveedora del servicio es que implementa a un contrato o interfase.
No es necesario modificar la clase consumidora si cambiamos o implementamos nuevas funcionalidades en la clase proveedora del servicio o lo único que se mantiene intacto es el contrato que se definió para colaborar entre ellas.
Se puede utilizar independientemente tanto la clase consumidora como el cliente porque no están fuertemente acopladas, esto también facilita la prueba o “testing” de cada uno de los componentes.
Veamos el patrón en acción en C# 2.0, tomado de un ejemplo de nuestro gran amigo y contribuidor a la gran familia de desarrolladores en la WWW David Hayden.public class


BlogDataSource:IBlogDataSource
{
private readonly ILogger _logger;
public BlogDataSource(ILogger logger)
{
_logger = logger;
}
public void AddBlog(Blog blog)
{
_logger.Log(blog.Title);
}
}


Cabe mencionar en este momento que existe 3 formas de implementar DI (Property Setter Injection, Contructor Injection and Interface or Method Injection), de las cuales si buscamos realizar testing de nuestros componentes la decisión indudable para la implementación será Constructor Injection. Revisemos el códigoanterior. El cliente o la clase consumidora para crear en memoria un objecto de esta clase necesita el constructor se lo indica una implementación (es decir otra clase) que implemente los requerimientos de la interface. Para ello cuando necesitamos probar esta clase que para el ejemplo es BlogDataSource si necesidad de tener la implementación de esta interface podemos crear una clase simulada o Mocking que nos sirva de apoyo para revisar el codigo de la clase que queremos probar, de esta manera nuestro componente BlogDataSource es dependiente en su creación en memoria o instanciación pero independiente en su prueba por ello nos podemos asegurarque la lógica que esconde nuestro componente BlogDataSource funciona o no funciona y no se afectado por la dependencia del otro servicio o componente representadopor su interface. Veamos como una clase cliente consumiria nuestro componente

BlogDataSource : public class ClientClass
{
private readonly IBlogDataSource _blogDataSource;

public void Main()
{ ILogger _serviceLogger = new MockingClassLoger(); // Siguiente linea estamos inyectando el servicio del cual depende nuestro otro componente.
IBlogDataSource blogDS = new Blog(_serviceLogger);
blogDS.AddBlog(new Blog());
// Internamente el metodo blog invoco el servicio Looger _logger.Log(blog.Title);
}
}


El punto a destacar aquí es como lo mencionamos anteriormente no necesitamos en nuestras "pruebas" de los servicios de los cualesdepende el componente que desamos probar, así podemos deducir facilmente responsabilidades esto facilita la mantenibilidad de las aplicaciones.En nuestro ejemplo lo que debe de hacer el componente BlogDataSource es invocar el servicio que implementa la interface ILogger si lo hizofunciona sino no funciona listo, es un ejemplo sencillo para poder ejemplificar el punto al que deseo explicar. Sin mas hemos terminado esta exposición sobre el tema de DI y espero continuar escribiendo sobre componentes y sus cualidades, testing que es uno de los objetivos de los mismos.

Recuerde Code4Fun!.

Manolo Herrera

2 comentarios:

Gerson Mayen dijo...

Manolo, ojala en algún momento te dé tiempo de poner un ejemplo completo, ya que en la charla que diste sobre Dependency Injection el día 21 de Noviembre, no te dio tiempo terminar el ejemplo, y buscando en la pagina antigua de la comunidad solo está habilitada la presentación de Power Point.

De igual forma me surge otra duda, en la charla de noviembre te entendí que la mejor implementación del patrón era Interface-based injection, pero este resumen indicas que es Constructor Injection.

Gracias.

Juan Manuel Herrera dijo...

Gracias por leer el tema. Tengo planeado seguir produndizando con el tema, para que se pueda apreciar la utilidad de este patron. Aclarando tu duda la forma de colaborar entre clases es a tráves de interfaces, DI lo hace siempre con una interface. Pero lo puede hacer en un setter es decir en una propiedad que solo permite colocar y no leer el valor, a tráves del constructor y a tráves de un metodo que también es llamado "Por interface" pero esto no quiere decir que los otros no lo utilicen todos usan interfaces por uno de ellos su nombre lo indica de esta forma.

Cual es mejor depende de la solución que cada uno necesite en un caso en especifico. En el constructor es util porque te facilita el testing y se ve claramente al incio los servicios de los cuales depende tu clase.

Pronto discutiré este tema en el blog.