Archive for the '14126' Category

IoC y DI: Hello, world con Spring Framework

Friday, August 13th, 2010

Ya vimos algo de Inversion of Control y Dependency Injection en:

Definiendo Inversion of Control y Dependency Injection
Introducción a IoC y DI: Otro ejemplo web
Introducción a IoC y DI: Un ejemplo web
Introducción a IoC y DI: Hello, world flexible

material producido dentro del Proyecto Hogwarts. En el post donde definía IoC y DI, mencioné la existencia de contenedores de IoC: utilitarios que facilitan el “armado” de los objetos y sus relaciones. En el primer post de la serie, describí el ejemplo más simple posible. Veamos ahora de poder levantar el mismo ejemplo apelando a un contendor de IoC.

Uno de los más conocidos, uno de los primeros, es el Spring Framework de .NET (port de la implementación inicial en Java). Veamos la estructura del ejemplo (que se puede bajar de HelloWorldSpringExample.zip).

 

Vemos que refiere a Spring.Core, una librería que les dejo incluida en el ejemplo. Seguimos teniendo nuestras interfaces IMessageProcessor, IMessageProvider, y nuestras clases concretas MessageProcessor y MessageProvider. Pero nuestro programa ahora ejecuta:

    class Program
    {
        static void Main(string[] args)
        {
            IApplicationContext context = ContextRegistry.GetContext();
            IMessageProcessor processor = (IMessageProcessor)context.GetObject("processor");
            processor.Process();
        }
    }

Spring tendrá a su cargo crear los objetos, a medida que los pedimos. El ContextRegistry es parte de Spring. Levanta información de los objetos que queremos trabajar. Para poder usarlo, tenemos que incluir los namespaces:

using Spring.Context.Support;
using Spring.Context;

En su forma más sencilla, como la de arriba, toma sus datos de la configuración:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <sectionGroup name="spring">
      <section name="context" 
      type="Spring.Context.Support.ContextHandler, Spring.Core"/>
    </sectionGroup>
  </configSections>
  <spring>
    <context 
      type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
      <resource uri="file://SpringConfig.xml"/>
      <!--<resource uri="assembly://HelloWorld1/HelloWorld1/EmbeddedSpringConfig.xml"/>-->
    </context>
  </spring>
</configuration>

Se definió un nuevo sectionGroup llamado spring, que es leído por la librería. Ahí se puede poner la configuración de nuestro ejemplo, pero elegí referir a un archivo aparte, así podemos cambiarlo sin tocar la configuración general de la aplicación. Vean que dejé comentado otra forma: incluir ese archivo de configuración como recurso embebido. El SpringConfig.xml está marcado en el proyecto como Copy to Output Directory Always.

El archivo SpringConfig.xml tiene:

<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns='http://www.springframework.net'>
  <object id='provider' type='HelloWorldSpringExample.MessageProvider'>
    <property name='Message' value='Hello, World'></property>
  </object>
  <object id='processor' type='HelloWorldSpringExample.MessageProcessor'>
    <property name='Provider' ref='provider'/>
  </object>
</objects>

Podemos definir cada objeto, darle:

– un id (nombre)

– un tipo (qué tipo concreto creamos)

– argumentos para su constructor

– valores para sus propiedades

Cuando especificamos un valor podemos poner value=”Hello, world” un valor en concreto, o podemos referir a otro objeto conocido y creable por Spring, usando ref=”provider”.

Entonces, cada objeto lo completamos inyectándole otros objetos via propiedades o argumentos en el constructor.

Notemos la forma en que la propiedad Provider del MessageProcessor es "inyectada": referenciando (atributo ref) a otro objeto que Spring conoce cómo crear.

De esta forma, se pueden levantar grafos de objetos complejos, pidiendo posiblemente uno desde nuestro programa. En este ejemplo, pedimos el objeto con id=processor. Automáticamente, Spring creó el objeto con id=provider, y le inyectó el mensaje que contiene.

Tenemos que ver otro ejemplo de Spring, tener algún ejemplo web, y usar otros contenedores, como Ninject o StructureMap. Y cuando tengamos más experiencia, volveremos a ver qué se consiguió con esto, y qué características tienen los contenedores IoC.

Nos leemos!

Angel “Java” Lopez

http://www.ajlopez.com

http://twitter.com/ajlopez

Definiendo Inversion of Control and Dependency Injection

Thursday, August 12th, 2010

Después de algunos ejemplos de código, dentro del Proyecto Hogwarts:

Introducción a IoC y DI: Otro ejemplo web
Introducción a IoC y DI: Un ejemplo web
Introducción a IoC y DI: Hello, world flexible

veamos de definir los términos Inversion of Control (Inversión de Control) y Dependency Injection (Inyección de Dependencias).

Recordemos lo que vimos en los ejemplos: los objetos no funcionan solos, forman un grafo de objetos. Algunos objetos colaboran con otros. Se dice entonces que los objetos consumidores tienen dependencia de los objetos que consumen como ayudantes. En una aplicación real, el mantener las dependencias puede irse complicando, debido a la cantidad de clases y responsabilidades. También, puede que querramos modificar las implementaciones. Vimos que nos conviene ir definiendo interfaces, y referirse a ellas, para no ligar los objetos consumidores a implementaciones concretas.

Inversion of Control refiere, entonces, a que el objeto consumidor no crea los objectos ayudantes, de los que depende. No se le adosa esa responsabilidad. Como vimos en los ejemplos, en esos objetos desaparece el "new" de los objetos ayudantes.

Dependency Injection se refiere a una forma de implementar IoC: el objeto consumidor recibe sus dependencias en propiedades o en argumentos de constructor.

Recordemos el ejemplo de servicio consumiendo repositorio, gráficamente:

El código de nuestro servicio no tenía new (con lo que tiene Inversion of Control). Pero bien podría haber pedido expresamente a otro que le diera el objeto, como en:

public class CustomerService
{
    private ICustomerRepository repository;
    public CustomerService()
    {
        this.repository = Registry.GetInstance<ICustomerRepository>();
    }
    public IEnumerable<Customer> GetCustomers()
    {
        return repository.GetAll();
    }
}

En este caso, sería simplemente Inversion of Control: alguien, la clase Registry, tendría la responsabilidad de ubicar o crear la instancia a usar. Pero CustomerService pide esa resolución explícitamente.

En cambio, el código de nuestro ejemplo era:

public class CustomerService
{
    private ICustomerRepository repository;
    public CustomerService(ICustomerRepository repository)
    {
        this.repository = repository;
    }
    public IEnumerable<Customer> GetCustomers()
    {
        return repository.GetAll();
    }
}

Acá apareció Dependency Injection: CustomerService ni siquiera pide el repositorio, alguien de afuera se lo da. Tenemos que estudiar que podemos usar librerías llamadas contenedores de IoC que nos facilitan la inyección automática de esas dependencias.

No necesariamente se inyecta un solo objeto: otras clases más complejas podrán necesitar de más objetos inyectados. Tenemos que investigar formas de inyectarle esas dependencias. Por ahora, lo vimos “manualmente”: vamos a ver que hay librerías llamadas IoC Containers, contenedores de Inversion of Control, que nos pueden ayudar a armar el grafo que necesitamos en un contexto (en producción, en pruebas TDD, etc…)


Artículos en inglés para consultar con más detalles:


Inversion of Control Containers and the Dependency Injection pattern (Martin Fowler)

Inversion of Control and Dependency Injection: Working with Windsor Container

Mis enlaces sobre el tema:

http://delicious.com/ajlopez/ioc

http://delicious.com/ajlopez/di

Nos leemos!

Angel “Java” Lopez

http://www.ajlopez.com

http://twitter.com/ajlopez

Introducción a IoC y DI: otro ejemplo web

Wednesday, July 28th, 2010

Sigamos explorando Inversion of Control y Dependency Injection, dentro del Proyecto Hogwarts. Anteriores posts del tema:

Introducción a IoC y DI: Un ejemplo web
Introducción a IoC y DI: Hello, world flexible

Simplemente para ver otro ejemplo sencillo de programación con interfaces, veamos de agregar un objeto presenter en un ejemplo web. La forma de llenar la página con datos (dar datos a la vista) y la forma de atender comandos del usuario (un botón de aceptar en un formulario para grabar lo ingresado), lo separamos del "code behind" y le asignamos esa responsabilidad a un objeto Presenter:

Notemos que la vista que va a consumir no es una clase concreta, sino una interfaz. Eso nos va a permitir cambiar la vista, o probar a nuestro objeto presenter sin necesidad de darle una página web para operar, sino algún otro objeto más adecuado para nuestras pruebas.
El código de este ejemplo se puede bajar de PresenterExample.zip.

Esta vez, la solución tiene dos proyectos de librería de clases, y una aplicación web:

 

El primer proyecto se basa en nuestro anterior ejemplo: contiene el servicio, ayudado por repositorios.

El segundo proyecto define la interfaz que tiene que cumplir todas las vistas de lista de Customers, ICustomerListView:

namespace Customers.Presenters {  public interface ICustomerListView
   {
      IEnumerable<Customer> Customers { set; }
   }
}

El Presenter consume tanto a la vista como al servicio:

 public class CustomerPresenter  {
    private ICustomerService service;
    private ICustomerListView view;
    public CustomerPresenter(ICustomerService service, ICustomerListView view)
    {
       this.service = service;
       this.view = view;
    }
    public void Initialize()
    {
      this.view.Customers = this.service.GetCustomers();
    }
 }

Vean que el servicio también lo ve como una interfaz. Finalmente, el código de la página arma todos los objetos y se "conecta" con el Presenter para que éste la maneje. En nuestro ejemplo, sólo para llenar sus datos:

  public partial class _Default : System.Web.UI.Page, ICustomerListView
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!this.IsPostBack)
            {
                CustomerPresenter presenter = new CustomerPresenter(new CustomerService(new InMemoryCustomerRepository()), this);
                presenter.Initialize();
            }
        }
        public IEnumerable<Customer> Customers
        {
            set
            {
                this.grdCustomers.DataSource = value;
                this.grdCustomers.DataBind();
            }
        }
    }

Este ejemplo es un paso más hacia el tema de esta serie de post: Inversion of Control y Dependency Injection, términos que tenemos que definir. Pero, como en el post anterior, hay algo para mejorar: en el código de la página decidimos qué objetos concretos conectamos en un grafo de objetos colaboradores. Lo que quisiéramos mejorar es la definición de esos objetos concretos: tratar de no tenerlo de alguna forma fijo en el código, sino tener alguna otra manera de instanciarlos y de cambiar cuáles son las clases concretas a usar.

Este material será incluido en el curso online del Southworks Professional Improvement Program en http://pip.southworks.net/ (pueden ir ahí para ir explorando el curso de TDD ya publicado).

Nos leemos!

Angel “Java” Lopez

http://www.ajlopez.com

http://twitter.com/ajlopez

Introducción a IoC y DI: Un ejemplo web

Wednesday, July 21st, 2010

Sigo posteando material de lo que se está produciendo en el Proyecto Hogwarts. Esta vez, sobre el tema de Inversion of Control y Dependency Injection. Dentro de poco, estará disponible un sitio en línea con un curso de TDD, y otros en construcción. En el anterior post:

Introducción a IoC, DI: Hello, world flexible

había planteado un ejemplo simple, veamos alguno apenas más complejo. Y veamos de plantearla con interfaces.

Lo que vimos en ese ejemplo es que:

– Un objeto A puede necesitar la ayuda de otro objeto B, pero conociendo la interface, sin ligarse a una clase en concreto

– Al objeto A alguien le provee el objeto B

Lo que vamos explorar ahora, es un ejemplo donde al objeto A le damos alguna vez un objeto B1, y otras veces el objeto B2. Para que no sea sólo una aplicación de consola, veamos de levantar el alcance, y hagamos un ejemplo web.

Sea una simple aplicación web, donde tenemos una página para presionar dos enlaces para leer y mostrar los clientes:

La idea es tener DOS implementaciones de una interface que se haga responsable de implementar un repositorio de clientes (digamos, una lista de clientes). Si presionamos en el primer enlace, se usará un repositorio en memoria:

 

Si presionamos en el segundo enlace, usaremos el repositorio de base de datos:

Pueden bajarse el ejemplo desde mi Skydrive CustomersExample.zip. Contiene una solución .NET y los scripts para crear la base de datos en SQL Server.

En este ejemplo, vamos a usar un servicio que se ayudará de un repositorio para conseguir los objetos que necesita la presentación. Veamos cómo se implementó.

Tenemos entonces, un servicio que necesita consumir un repositorio. Como en el ejemplo de Hello, world, el objeto consumidor no referencia a una clase concreta, sino a una interfaz:

Esa interfaz está definida como:

namespace Customers
{
 public interface ICustomerRepository
 {
 Customer GetById(int id);
 void Insert(Customer customer);
 void Update(Customer customer);
 void Delete(Customer customer);
 IQueryable<Customer> GetAll();
 }
}

Luego tenemos dos implementaciones de esta interfaz:

Una es la implementación del repositorio en memoria, parte del código:

 public class InMemoryCustomerRepository : ICustomerRepository
 {
 private IList<Customer> customers = new List<Customer>();
 public InMemoryCustomerRepository()
 {
 for (int k = 1; k <= 10; k++)
 {
 customers.Add(new Customer()
 {
 Id = k,
 Name = string.Format("Customer {0}", k),
 Address = string.Format("Address {0}", k),
 Notes = string.Format("Notes {0}", k)
 });
 }
 }
//....
 }

La otra implementación de la interfaz accede a una base de datos, código parcial:

 public class DatabaseCustomerRepository : ICustomerRepository
 {
 private string connectionstring;
 public DatabaseCustomerRepository(string connectionstring)
 {
 this.connectionstring = connectionstring;
 }
 public Customer GetById(int id)
 {
 SqlConnection conn = new SqlConnection(this.connectionstring);
 SqlCommand cmd = new SqlCommand("select Id, Name, Address, Notes from Customer where Id = @Id", conn);
 cmd.Parameters.Add(new SqlParameter("@Id", id));
 conn.Open();
 SqlDataReader reader = cmd.ExecuteReader();
 Customer customer = null;
 if (reader.Read())
 customer = this.GetCustomer(reader);
 reader.Close();
 conn.Close();
 return customer;
 }
///...
 }

Lo que se usó en este ejemplo es que al crear el objeto servicio le pasamos en los argumentos del constructor cuál implementación de repositorio queremos usar. Así, en la acción de ver por memoria, el código es:

protected void lnkMemory_Click(object sender, EventArgs e)
 {
 CustomerService service = new CustomerService(new InMemoryCustomerRepository());
 this.grdCustomers.DataSource = service.GetCustomers();
 this.grdCustomers.DataBind();
 }

La otra opción es por base de datos:

 protected void lnkData_Click(object sender, EventArgs e)
 {
 CustomerService service = new CustomerService(new DatabaseCustomerRepository("server=.\SQLEXPRESS;database=Customers;integrated security=true"));
 this.grdCustomers.DataSource = service.GetCustomers();
 this.grdCustomers.DataBind();
 }

Notemos que al servicio le "inyectamos" su ayudante, esta vez usando un parámetro de su constructor (en el ejemplo Hello World, habiamos inyectado usando propiedades).

Esto nos muestra:

– La capacidad de abstraer lo que necesitamos (la interfaz de repositorio) de cómo lo implementamos

– Nos permite cambiar la implementación y que todo siga funcionando igual

Acá usamos las DOS implementaciones al mismo tiempo. Pero es más común usar solo una.

Podríamos haber codificado el acceso a los clientes en el propio servicio. Pero lo separamos para:

– Poder mejorar el servicio (p.ej., en temas de seguridad) separando esas otras funcionalidades de la más simple de recuperación de clientes.

– Poder cambiar el ayudante, sin necesidad de cambiar el servicio y sus consumidores.

Hasta podemos ir armando una aplicación de demostración, usando durante semanas la implementación del repositorio en memoria. Mientras, en un desarrollo ágil, vamos iterando, mejorando nuestro dominio, SIN tener una base de datos por debajo. El cliente final va viendo nuestro avance, y solamente cuando tenemos algo más definido, vamos definiendo la base de datos subyacente y operando sobre ella.

También, este "approach" nos permite cambiar la implementación de la base de datos. Ahora, la implementación va contra SQL Server, pero si mañana necesitamos ir contra Oracle, cambiamos la implementación, y ni servicio y otros se enteran de ese cambio.

Y por último, veremos, más adelante en otro curso (Mocks con TDD), que podemos usar implementaciones de repositorios que no vayan contra la base, para alivianar nuestros tests del servicio.

Y todo, por haber programado contra la interfaz, y segregado responsabilidades.

Tenemos que avanzar en un sentido: no queremos tener que cambiar el código de creación del servicio, para que una vez use una implementación del repositorio (en memoria) y otra vez use una distinta (en base de datos). Quisiéramos que el cambiar esa relación NO IMPLIQUE tener que cambiar el código. Esa es la idea que subyace en lo que tenemos que definir en los próximos post de este tema: qué es Inversion of Control, qué es Dependency Injection, y contenedores de IoC.

Nos leemos!

Angel “Java” Lopez

http://www.ajlopez.com

http://twitter.com/ajlopez

Introducción a IoC y DI: Hello, world, flexible

Tuesday, July 13th, 2010

Dentro del Proyecto Hogwarts, estamos produciendo material para un curso de Inversion of Control y Dependency Injection, que quedará en línea (además de preparar alguna charla o taller presencial). No voy a explicar todavía los términos, sino que quisiera seguir otro camino: tomando la aplicación más simple, ir viendo de flexibilizarla, hasta llegar en algún momento a usar IoC y DI (siglas de los términos en inglés para Inversión de Control e Inyección de Dependencias).

¿Cuál es la aplicación más simple? Tomemos un clásico “Hello, world” en C# (espero publicar más adelante una serie de versiones en Java, que culmine con el uso de Spring Framework, como hago en mis cursos sobre el tema):

 class Program
 {
 static void Main(string[] args)
 {
 Console.WriteLine("Hello, world");
 }
 }

¿Qué podemos querer cambiar de esa aplicación? Se me ocurren dos cosas:

– El mensaje

– La forma de procesarlo

Son dos responsabilidades, que podemos asignarlas a dos clases nuevas, digamos, MessageProvider:

 public class MessageProvider
 {
 public string Message { get; set; }
 }

y a MessageProcessor:

 public class MessageProcessor
 {
 public MessageProvider Provider { get; set; }
 public void Process()
 {
 Console.WriteLine(this.Provider.Message);
 }
 }

Nuestro Hello World puede quedar ahora de esta forma:

 static void Main(string[] args)
 {
 MessageProvider provider = new MessageProvider() { Message = "Hello, World" };
 MessageProcessor processor = new MessageProcessor() { Provider = provider };
 processor.Process();
 }

Vean que creamos los dos objetos de dos clases concretas, y los “conectamos”: como el MessageProcessor necesita de un MessageProvider, se lo proveemos mediante una propiedad (podríamos haberlo hecho mediante un parámetro en el constructor). Lo que estamos haciendo es construir un grafo de objetos, y dada la forma en que colaboran entre ellos, el grafo lo armamos nosotros. La alternativa sería: DENTRO de MessageProcessor crear un MessageProvider y usarlo. Pero eso dejaría acoplada la responsabilidad de cuál mensaje procesar al propio MessageProcessor. De la forma adoptada arriba, si queremos cambiar el mensaje, NO ALTERAMOS el código de MessageProcessor. A este objeto “le llueve del cielo” su colaborador. Vemos la aparición de un camino a seguir: los “new” de los objetos colaboradores, no están en el código de los consumidores de esos colaboradores. Más adelante, pasaremos a ejemplos más de la vida real. Pero por ahora, exploremos estos temas con ejemplo sencillo. Luego, el problema (armar el grafo de objetos) y la solución (lo armamos “por fuera” de los objetos) será la misma, con variantes, en ejemplos medios y complejos.

Próximo paso: ahora que tenemos objetos que se hacen cargo de las responsabilidades que descubrimos, podemos refinar el ejemplo: ahora, MessageProcessor está acoplado a una clase concreta MessageProvider. Pero ¿qué necesita realmente MessageProcessor? En lugar de consumir un objeto de una clase concreta, ahora que tenemos un mini caso de uso codificado, podemos extraer, “descubrir” la interface que necesita consumir MessageProcessor. Y ya que estamos, también extraer, descubrir la interface, la conducta expuesta de cualquier MessageProcessor que se nos ocurra mañana. Pueden hacerlo a mano, o apelar a las capacidades de Refactoring, Extract Interface de Visual Studio. Tenemos IMessageProvider:

 public interface IMessageProvider
 {
 string Message { get; set; }
 }

IMessageProcessor:

 public interface IMessageProcessor
 {
 void Process();
 IMessageProvider Provider { get; set; }
 }

Y nuestras clases ahora implementan y esperan esas interfaces:

 public class MessageProvider : HelloWorldInterfacesExample.IMessageProvider
 {
 public string Message { get; set; }
 }
 public class MessageProcessor : HelloWorldInterfacesExample.IMessageProcessor
 {
 public IMessageProvider Provider { get; set; }
 public void Process()
 {
 Console.WriteLine(this.Provider.Message);
 }
 }

Vemos que MessageProcessor es UNA implementación de IMessageProcessor, y que no espera un MessageProvider, sino que se las arregla con cualquier implementación, actual o futura, de IMessageProvider. Como antes, él no se preocupa de crear esa instancia ayudante, sino que alguien proveerá.

Nuestra invocación queda:

 static void Main(string[] args)
 {
 IMessageProvider provider = new MessageProvider() { Message = "Hello, World" };
 IMessageProcessor processor = new MessageProcessor() { Provider = provider };
 processor.Process();
 }

Por ahora, no parece que ganemos mucho (para este ejemplo simple). Si tenemos que cambiar el proveedor del mensaje, el mensaje, o la forma de procesarlo (por ejemplo, que genere, en lugar de un mensaje en pantalla, otra cosa, como un archivo PDF o una página web), tenemos que tocar el código de nuestra rutina de inicio. Exploraremos cómo, ayudados por algún framework, conseguir que ese armado y esos datos se deleguen a configuración.

Código de los ejemplos en HelloWorldObjectsExample.zip y HelloWorldInterfacesExample.zip

Nos leemos!

Angel “Java” Lopez

http://www.ajlopez.com

http://twitter.com/ajlopez