HolaMundo IoC, Inyección de dependencias con Spring .net

by

DI con Spring
Si has oído hablar del patrón de Inversion Of Control (IoC), o Dependency Injection, pero no terminas de entenderlo. Si eres conocedor de Spring .net y sus numerosas ventajas, pero lo ves enorme y no sabes como hacer un sencillo ejemplo de Inyección. Y si además (y esto ya es la leche!) eres uno de esos de la secta del Guille y te gusta programar en Visual Basic, espero que este post te pueda ayudar 🙂

Indice

Qué es eso de dependencia?
Usando interfaces
El patrón factory
Inyectando con Spring .net
Y esto para que nos sirve en el mundo real?

¿¿Qué es eso de dependencia??

Seguramente habrá alguna explicación teórica “potente”, pero vamos a reducirlo al siguiente ejemplo de código:


''' <summary>
''' Start.vb
''' </summary>
''' <remarks></remarks>
Module Start
    Sub Main()

        'Inicializamos datos de entrada
        Dim lista As New List(Of String)
        lista.Add("Temperatura")
        lista.Add("Altura")
        lista.Add("Espesor")

        'Defino el objeto centro que va a procesar
        'el listado de entrada
        Dim centro As CentroA
        centro = New CentroA

        'Procesamos!
        centro.Procesa(lista)
    End Sub
End Module

¿Vemos la dependencia? Pues no es, ni más ni menos, que el módulo start.vb, tiene una dependencia de la clase CentroA. Sí, la dependencia esta en las líneas:


        Dim centro As CentroA
        centro = New CentroA

Digamos que nos estamos “casando” con la clase CentroA y su forma de procesar el listado de entrada. Si en el futuro, no nos gusta como la clase procesa nuestra lista, o bien cambiamos el método “Procesa” de la clase, o bien cambiamos las 2 líneas anteriores y utilizamos una nueva clase. Luego os cuento un par de casos prácticos donde entenderéis mejor la utilidad de la “no dependencia”.

Bien, ¿cómo podemos ir desacoplándonos de la clase CentroA?

Usando Interfaces

Un primer paso es utilizar interfaces. Sigamos con el código fuente.


Public Class CentroA
    Implements ICentro

    Public Sub Procesa(ByVal listado As List(Of String)) _
        Implements ICentro.Procesa
        '... como antes

Y en el método principal tendremos:


        Dim centro As ICentro
        centro = New CentroA

Bien, hemos desacoplado algo nuestro método principal, la definición de centro ya no depende de la clase CentroA (sí, lo sé, no es gran cosa, pero sigamos :)).
Si ahora queremos cambiar la forma de procesar nuestro listado, podemos definir una segunda clase que implemente la interfaz:


''' <summary>
''' CentroB.vb
''' </summary>
''' <remarks></remarks>
Public Class CentroB
    Implements ICentro

    Public Sub Procesa(ByVal listado As List(Of String)) _
        Implements ICentro.Procesa
        Try
            For Each elem In listado
                Console.WriteLine("Item: {0} procesado en centro B", elem)
            Next
        Catch ex As Exception
            Throw
        End Try
    End Sub

End Class

Y ahora cambiamos de nuevo nuestro método principal:


        centro = New CentroB

Sí, nuestro método principal sigue dependiendo de la clase CentroA o CentroB (o cualquier que implemente la interfaz). ¿Cómo podemos desacoplar algo más esta dependencia?

El patrón Factory

Con el patrón Factory, lo que hacemos es delegar la dependencia, a otra clase. Aquí tenéis el UML:

factory_diagrama_uml

factory_diagrama_uml

Si os parece algo lioso, vamos a seguir con el ejemplo.
Creamos una nueva clase (atención a los comentarios):

''' <summary>
''' FactoryCentro.vb
''' </summary>
''' <remarks></remarks>
Public Class FactoryCentro

    ''' <summary>
    ''' En funcion de la opcion que recibe como parametro
    ''' devuelve un objeto de cualquier clase
    ''' que implemente la interfaz ICentro.
    ''' En nuestro caso CentroA o CentroB
    ''' </summary>
    '''
<param name="opcion"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function DameObjetoCentroDeseado(ByVal opcion As Short) As ICentro
        Try
            Select Case opcion
                Case 1
                    Return New CentroA
                Case 2
                    Return New CentroB
                Case Else
                    Throw New ArgumentOutOfRangeException("Opcion", "Opcion no soportada")
            End Select
        Catch ex As Exception
            Throw
        End Try
    End Function
End Class

Ahora tenemos una clase que nos resuelve la dependencia, tan solo debemos decirle que clase queremos utilizar, a través del parámetro opcion. Si vemos como quedaría el método principal, tenemos:

Dim opcion As Short = 1
Dim centro As ICentro
centro = FactoryCentro.DameObjetoCentroDeseado(opcion)

Esto ya pinta mejor, no?. Ahora ya no tenemos ninguna dependencia en el metodo principal y para procesar nuestro listado, tan solo debemos indicar la opción que queremos utilizar. En esta caso la he puesto “a piñón”, pero si eso mismo, lo sacamos a un archivo de configuración, la cosa ya pinta muy desacoplada, ¿verdad? Os paso el resultado con opcion=1 y luego opcion=2:

resultado1
resultado2

Inyectando con Spring .net

Esto mismo se puede resolver desde Spring.net de forma algo más sencilla y elegante. Antes de nada, tenemos que tener instalado el framework, que lo podéis descargar desde aquí.

Una vez instalado, agregamos una referencia al archivo Spring.Core.dll. Tras esto, realizamos la configuración de Spring, para la inyección que buscamos. Esta config puede realizarse de varias maneras: mismo código fuente, archivo .xml inependiente, o el propio archivo de configuración de la aplicación. Yo lo he hecho en este último. Os pongo el código y lo comentamos:

<configuration>
  <configSections>
    <!--Spring .net-->
    <sectionGroup name="spring">
      <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
      <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
    </sectionGroup>
    <!--Spring .net END-->
  </configSections>

  <spring>
    <context>
      <resource uri="config://spring/objects"/>
    </context>

    <objects xmlns="http://www.springframework.net" >      <description>Ejemplo IoC Spring.net</description>      <object id="CentroIdA"              type="PruebaSpringID.CentroA, PruebaSpringID">      </object>
      <object id="CentroIdB"              type="PruebaSpringID.CentroB, PruebaSpringID">      </object>
    </objects>
  </spring>

</configuration>

La parte del configSections, es simplemente para añadir la sección de spring. Dentro del nodo spring, tenemos el resource, que indica “dónde” se encuentra la configuración de spring. Como os decía, en este caso se encuentra en el mismo app.config, y eso es lo que le indicamos con uri=”config://spring/objects”.

En el nodo de objects, tenemos definidos 2 objetos, uno para la clase CentroA y otro para CentroB. Como id, ponemos el nombre que luego utilizaremos desde el código para inyectarlo, mientras que como type, hay que poner el “fully qualified name” del ensamblado.

Si no tenéis muy claro como sacar el FQN, podéis ir a las propiedades del proyecto, y en la pestaña de aplicación tendréis un “Nombre del ensamblado” y un “Espacio de nombres de la raíz”.

El FQN se compone: Espacio de nombres de la raíz + “.Nombre de la clase“, Nombre del ensamblado

Una vez configurada la parte de spring, veamos cómo utilizarlo desde código:

        Dim centro As ICentro
        'Cargamos el contexto de Spring .net
        Dim ctx As IApplicationContext = ContextRegistry.GetContext()

        'Definimos el id del objeto segun config de Spring
        Dim idSpringClase As String = "CentroIdA"
        'Inyectamos con Spring!!!
        centro = ctx.GetObject(idSpringClase)

        'Procesamos!
        centro.Procesa(lista)

Ah, no nos olvidemos de los imports necesarios:

Imports Spring.Context
Imports Spring.Context.Support

Si ejecutamos, y jugamos con el valor de “idSpringClase”, veremos el mismo resultado que al aplicar el patrón Factory.

¿Y esto para qué nos sirve en el mundo real?

Bueno, si nos centramos en el patrón Factory, o en como Spring consigue la inyección de dependencias (Spring es muy grande y tiene muchas otras posibilidades), la principal ventaja de esto es conseguir un código menos acoplado (loosely coupled), lo que de por sí, nos va a facilitar la reutilización y mantenimiento en general.

Un claro ejemplo de aplicación de esto en el mundo real, sería una aplicación que queremos que sea independiente del motor de base de datos sobre el que trabaja. Es decir, podemos definir una interfaz con las operaciones que nuestra aplicación realizará contra la BD (CRUD y demás), y luego implementamos esa interfaz con tantas clases como BD queramos “soportar” (una para trabajar con SQLServer, otra con Oracle, otra con MySql…). Finalmente, si nuestro motor de BD es MySQL, pues inyectamos la clase que trabaja con MySQL. Si el día de mañana aparece Postgre, pues creamos una nueva clase que implemente la interfaz y que trabaje con Postgre y a funcionar sin tocar el resto de la aplicación (con una buena arquitectura en capas).

Otra gran ventaja de inyectar dependencias es que facilita el testeo de una aplicación. Os pongo un ejemplo real en el que he trabajado y me ha funcionado muy bien:

Tenía un proyecto donde era necesario integrarme con SAP, invocar uno de sus RFCs y tratar el resultado. En las primeras fases del desarrollo, no tuvimos acceso a un servidor SAP, así que nos definimos una interfaz con el método de SAP, y desarrollamos 2 clases que la implementaban. Una de las clases tenía un “simulador” y el método de SAP, devolvía un valor inventado (en realidad, leíamos un XML con info simulada). La otra clase, tenía toda la integración con SAP y la llamada a un servidor de SAP real. De esta forma, en desarrollo, inyectábamos la clase simulada y pudimos probar el ciclo completo de la aplicación.

Y con esto terminamos, espero que os ayude a entender como desacoplar vuestros desarrollos con Spring y el IoC / DI. Si tenéis cualquier duda, dejad un comentario.

Saludos!!

Anuncios

Etiquetas: ,

6 comentarios to “HolaMundo IoC, Inyección de dependencias con Spring .net”

  1. Francisco Lozano Says:

    Un post muy ilustrativo… aunque el ejemplo de las BD’s… mejor usar NHibernate!!!

  2. Felipe Says:

    Hola!… talvez en el ejemplo de las bases de datos el NHibernate es mejor opción, pero, ¿qué sucede si requieres trabajar desde ciertos módulos con NHibernate y de otros con ADO?

    Saludos,

    Felipe

    • Luis Mañez Says:

      Hola Felipe,
      la verdad es q habría q ver con más detalle tu caso para ver si convendría usar en ambos casos nHibernate. Si no, se me ocurre que crees 2 repositorios, y que en cada módulo, inyectes el repositorio que toca (el que trabaja con nHibernate, o el que trabaja con ADO).

      Espero haberte ayudado algo.
      Saludos.

  3. Sprint.Net Application Framework « Santimacnet's Blog Says:

    […] este articulo de Luis Mañez, teneis explicado perfectamente con ejemplos de codigo real en C# exactamente los […]

  4. alex Says:

    Uff viejo gracias, muy bueno

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s


A %d blogueros les gusta esto: