25 mayo 2010

Inyección de Dependencias

En los comienzos de la programación, los programas eran lineales. El flujo de ejecución era simple y predecible, ejecutándose línea tras línea.
Aparecieron dos conceptos para estructurar el código: la modularidad y la reutilización de los componentes: se crean bibliotecas de componentes reutilizables. El flujo se complica, saltando de componente a componente, y aparece un nuevo problema: la dependencia (acoplamiento) entre las clases.
El problema de la dependencia se empieza a considerar lo suficientemente importante como para definir nuevos conceptos en el diseño.

Inyección de Dependencias (en inglés Dependency Injection, DI).

Originalmete, Inyección de Dependencias era referido por el nombre de Inversión de Control pero en un articulo escrito a inicios del 2004, Martin Fowler pregunto que aspectos de control son invertidos. Martin Fowler concluyo que la adquisición de dependecias era lo que se invertía. Basado en este hecho el definió que la frase "Inyección de Dependencias" sería un mejor termino utilizado de aquí en adelante.

Cualquier aplicación no trivial esta compuesta de dos o mas clases que colaboran para lograr un objetivo de la lógica del negocio. Tradicionalmente cada objeto es responsable de obtener sus propias referencias de los objetos que colaboran con él. Esto puedo llevarnos a acoplamientos rígidos y código difícil de probar.

Cuando aplicamos Inyección de Dependencias a los objetos le son proporcionadas sus dependencias en el tiempo de su creación por una entidad externa que coordina cada objeto en el sistema. En otras palabras, las dependencias son inyectadas en los objetos. En este sentido la Inyección de Dependencias significa una inversión de responsabilidad con respecto de como el objeto obtiene las referencias  de los objetos con los que colabora.

El principal beneficio de la Inyección de Dependencias es el bajo acoplamiento. Si un objeto solo conoce sus dependencias por una interface (no sus implementaciones o como están instanciadas) entonces las dependencias pueden intercambiarse con una implementación diferente sin que los objetos dependientes conozca sus diferencias.

Por ejemplo sin una clase Banco solo conoce a su clase dependiente Cliente a través de su interface entonces la implementación actual de Cliente no tiene importancia para la clase Banco. Cliente podría ser un Pojo, un Webservice, un Ejb, o un objeto Mock para una prueba unitaria. como comentamos la clase Banco no le interesa saber de que tipo es la implementación de la clase Cliente.


En cierto modo es una implementación del Principio de Hollywood, una metodología de diseño de software, cuyo nombre proviene de las típicas respuestas que se les dan a los actores amateurs en las audiciones que tienen lugar en la meca del cine: no nos llames; nosotros te llamaremos.

24 mayo 2010

Sustitución de Liskov

Principio de Sustitución de Liskov (The Liskov Substitution Principle, LSP)

Clases derivadas deben ser sustituibles por sus clases bases. Este principio trata acerca de aplicar la herencia correctamente para lograr un buen diseño. Cuando se hereda de una clase base, debemos ser capaces de sustituir las subclases por la clase base sin que suceda ningún tipo de problema o errores, de otro modo podemos asegurar que la herencia se esta aplicando incorrectamente.

Otras opciones que tenemos para relacionar clases son la Delegación, Composición y Agregación.

Delegación.
Es cuando transferimos la responsabilidad o tarea de hacer algo a otra clase o método. Si necesitamos usar la responsabilidad de una clase, pero no queremos cambiar esa responsabilidad podemos considerar la delegación en lugar de la herencia.

La Delegación nos permite que nuestra aplicación sea débilmente acoplada, es decir que un objeto de cara al exterior expresa cierto comportamiento pero en realidad delega la responsabilidad de implementar dicho comportamiento a un objeto asociado en una relación inversa de responsabilidad.

Composición.
Se utiliza para expresar que un par de objetos tienen una relación de dependencia para llevar a cabo su función, de modo que uno de los objetos involucrados está compuesto por el otro. De manera práctica, es posible reconocer asociatividad entre dos objetos A y B si la proposición "A tiene un B" es verdadera. Por ejemplo: "una computadora tiene un disco duro" es verdadero, por tanto un objeto computadora tiene una relación de composición con al menos un objeto disco duro.

La composición es más poderosa cuando se usa un comportamiento definido en una interface, entonces se escoge de una variedad de implementaciones de esa interface permitiéndonos cambiar el comportamiento a tiempo de ejecución.

Una característica importante de la composición es que el objeto compuesto del comportamiento de otros objetos maneja el ciclo de vida de sus objetos compuestos, es decir, cuando el objeto compuesto es destruido también son destruidos todos los objetos que lo componen, los objetos que componen al objeto compuesto no existen fuera de este objeto.

Agregación.
Que sucede cuando queremos todos los beneficios de la composición, flexibilidad al cambiar el comportamiento a tiempo de ejecución, pero necesitamos que los objetos existan fuera del objeto principal, la respuesta es utilizar la agregación.
La agregación es cuando una clase es usada como parte de otra clase, pero puede existir si la clase que la contiene deja de existir.

Veamos un ejemplo de Delegación, Composición y Agregación:


  • Un Almacén posee Clientes y Cuentas (los rombos van en el objeto que posee las referencias).
  • Cuando se destruye el Objeto Almacén también son destruidos los objetos Cuenta asociados, en cambio no son afectados los objetos Cliente asociados.
  • La composición se destaca por un rombo relleno.
  • La agregación se destaca por un rombo transparente.  
  • La flecha en este tipo de relación indica la navegabilidad del objeto referenciado. Cuando no existe este tipo de particularidad la flecha se elimina. 
  • La clase Cliente delega a la clase Banco el pago.
Debemos tener mucho cuidado en nuestros diseños al querer utilizar la herencia y decidir si la Delegación, Composición y Agregación son mejores alternativas que la herencia, ya que nuestros software será mas flexible, fácil de mantener, extender y reusar.