Friday, 17 April 2009

Dependency Injection - da bomb!

Inyección de Dependencias (DI) es un patrón de arquitectura orientado a objetos, en el que se inyectan objetos a una clase en lugar de ser la propia clase quien cree el objeto. Todo este concepto está muy bien explicado en el libro del Google Guice: Agile Lightweight Dependency Injection Framework, donde aparecen varios ejemplos de inyección y las descripciones de todo este palabreo friki como "framework" o "Inversion of Control IoC". Es este post encontrareis un buen ejemplo y simple sobre el uso de la inyección de dependencias, que además he combinado con el Chaining Method para darle más "jugo". Este ejemplo, se basa en un constructor de edificios (BuilderConstructor), que tiene que construir una casa, con un par de ventanas y una puerta. El ejemplo es básico, y mediante proveedores de casas, puertas y ventanas, le daremos las herramientas necesarias para que el constructor de edificios pueda crear la casa sin problemas.

El ejemplo en UML es el siguiente:

Disponemos de la clase House, Window i Door. La casa puede contener Windows y Doors, y estas pueden tener el tamaño que queramos.

En el BuilderConstructor, se inyectan los proveedores de House, Door y Window. El BuilderModule, es el que hace referencia y enlaza los proveedores con las diferentes clases, por lo tanto el Proveedor de puertas está enlazado con la clase Door y así con las demás clases.

Con el BuilderTest, crearemos la aplicación y las dependencias (Inyectores) con guice para que nuestro constructor pueda construir la casa.

La implementación del Provider, simplemente es una pequeña factoría de clase que Guice invocará cuando necesite una instancia de ese tipo.

  • Ejemplo de inyección @Inject:
House.java



import java.util.*;

public class House {
private ArrayList<Window> windows = new ArrayList<Window>();
private ArrayList<Door> doors = new ArrayList<Door>();

public House addWindow(Window window) {
this.windows.add(window);
return this;
}

public House addDoor(Door door) {
this.doors.add(door);
return this;
}

public String Description() {
String text;
text = "This is a house with " + windows.size() + " Windows and \n";
text = text + doors.size() + " Doors, the Windows are: \n";
Iterator<Window> winIterator = windows.iterator();
while (winIterator.hasNext()) {
text = text + (" " + winIterator.next().size() + " \n");
}
text = text + "and the Doors are \n";
Iterator<Door> doorIterator = doors.iterator();
while (doorIterator.hasNext()) {
text = text + (" " + doorIterator.next().size() + " \n");
}
return text;
}
}




Window.java



public class Window {
private int width;
private int height;

public Window(int width, int height) {
setWidth(width);
setHeigth(height);
}

public String size() {
return "Size " + height + "x" + width;
}

public void setWidth(int width) {
this.width = width;
}

public int getWidth() {
return width;
}

public void setHeigth(int height) {
this.height = height;
}

public int getHeigth() {
return height;
}
}




Door.java



public class Door {
private int width;
private int height;

public Door(int width, int height) {
setWidth(width);
setHeigth(height);
}

public String size() {
return "Size " + height + "x" + width;
}

public void setWidth(int width) {
this.width = width;
}

public int getWidth() {
return width;
}

public void setHeigth(int height) {
this.height = height;
}

public int getHeigth() {
return height;
}
}




HouseProvider.java, WindowProvider.java and DoorProvider.java



import com.google.inject.Provider;

public class HouseProvider implements Provider<House> {
public House get() {
return new House();
}
}

import com.google.inject.Provider;

public class WindowProvider implements Provider<Window> {
public Window get() {
return new Window(10, 10);
}
}


import com.google.inject.Provider;

public class DoorProvider implements Provider<Door> {
public Door get() {
return new Door(30, 10);
}
}




BuilderModule.java



import com.google.inject.AbstractModule;

public class BuilderModule extends AbstractModule {
protected void configure() {
bind(Window.class).toProvider(WindowProvider.class);
bind(Door.class).toProvider(DoorProvider.class);
bind(House.class).toProvider(HouseProvider.class);
}
}




BuilderConstructor.java



import com.google.inject.Inject;
import com.google.inject.Provider;

public class BuilderConstructor {
@Inject
private Provider<House> houseProvider;
@Inject
private Provider<Door> doorProvider;
@Inject
private Provider<Window> windowProvider;

public House getHouse() {
return houseProvider.get();
}

public Door getDoor() {
return doorProvider.get();
}

public Window getWindow() {
return windowProvider.get();
}
}




BuilderTest.java



import com.google.inject.Guice;
import com.google.inject.Injector;

public class BuilderTest {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new BuilderModule());
BuilderConstructor m = injector.getInstance(BuilderConstructor.class);
House house = m.getHouse();
house.addDoor(m.getDoor()).addWindow(m.getWindow())
.addWindow(m.getWindow());
System.out.println(house.Description());
}
}




El resultado de ejecutar esta aplicación es el siguiente:

This is a house with 2 Windows and
1 Doors, the Windows are:
Size 10x10
Size 10x10
and the Doors are
Size 10x30

Por lo tanto, aquí tenemos nuestra aplicación con inyección. En el BuilderConstructor, se añaden los @Inject en las variables de tipo Provider, y estas se crear automáticamente cuando se hace la inyección.

De todas maneras, esto es solo la punta del iceberg, hay muchas más opciones para aprender dentro del tema del DI. Dependency Injection minimiza la complejidad a través de la abstracción, y permite que tengamos una aplicación más modular y nos permite hacer el testing de una manera más fácil.

  • Enlaces de interés:
http://lebensold.net/category/php
http://misko.hevery.com/2008/07/08/how-to-think-about-the-new-operator/
http://martinfowler.com/articles/injection.html

0 comments:

Post a Comment