Introducción al Inversion of Control Container (IoC)

Introducción al Inversion of Control Container (IoC)
IntroductionIoCContainer

Hola geeks! Hoy vamos a seguir con un titular bastante interesante que es Inversion of Control. Como en el anterior artículo, vamos a explicar un poco su teoría y luego haremos un ejemplo práctico. IoC Container con ayuda de Dependency Injection podemos automatizar las instancias y la duración de vida de las clases a las que nos hemos registrado. Lo destacable es que, podemos automatizar las instancias e inyecciones de las clases que necesitan parámetros. Esto nos facilita la utilización de los Dependency injection, pero no es obligatorio utilizar este método. Resumiendo, IoC nos permite instanciar clases y olvidar sus dependencias de otras clases que tiene. Lo que hacemos es declarar el IoC Container al principio y registramos todas las clases al contenedor. Luego por Dependency Injection, vamos pasando dicho contenedor a todos los Constructores que necesitemos utilizarlo. Así podemos recuperar fácilmente cualquier clase en cualquier otra clase y crear su instancia. IoC también es famoso por el principio de HollyWood: "don’t call us, we’ll call you" (No nos llames, nosotros le llamaremos.)

Dependency Injection (DI)

Muchas veces nos confundimos entre Inversion of Control y Dependency Injection. Muchos pensamos que son lo mismo. Pero en realidad son concepto diferente que comparten cosas en común. Dependency Injection no es nada más que la inyección de objetos en el constructor de una clase. Aquí podemos verlo en un ejemplo: public class MainViewModel : ViewModelBase { private readonly IInteractionService interactionService; public MainViewModel (IInteractionService interactionService) { this.interactionService = interactionService; } } En este caso, la responsabilidad de instanciar IInteractionService no recae a MainViewModel. Así podemos desviar la responsabilidad a una clase externa. En el Constructor podemos inyectar como Dependency Injection un Service, Factory o cualquier tipo de clase. Esto también lo utilizamos en MVVM, en concreto a la hora de crear clases para servicios. Para escribir un programa capaz de tener Unit Test, necesitamos clases de servicios para abstraer el código específico de cada plataforma o tarea. Después en la clase principal, solo tenemos que implementar el servicio correcto quienes son fácilmente intercambiables. Ahora vamos a mencionar dos de los más famosos IoC Container Frameworks para mostrar ejemplos.

Unity

Éste IoC Container fue creado por Microsoft Pattern & Practices. public class Bootstrapper { private IUnityContainer container; public Bootstrapper() { UnityContainer = new UnityContainer(); container.RegisterInstance<IInteractionService>(new Interaction-Service()); container.RegisterInstance<IConfirmationService>(new Interaction-Service()); container.RegisterInstance<IProjectExplorer>(new ProjectExplorerView-Model(serializableTypes, messenger, taskService)); container.RegisterInstance<IActionCreator>(actionCreator); var mainWindow = new MainWindow(); var viewModel = new MainViewModel(container); mainWindow.DataContext = viewModel; mainWindow.Show(); } } Unity Framework tiene un licencia de MS-PL Licence. Fue escrito por Chris Tavares y su "Kernel" está basado en ObjectBuilder. Si observamos el código, necesitamos una instancia de IUnityContainer. Después podemos registrar nuestras clases con un Interface (si tienen uno, no es obligatorio) con el método RegisterInstance que tiene IUnityContainer. La clase Bootstrapper, se llamará al principio en nuestro App.cs donde hayamos definido un método para ejecutar una lógica antes que la instancia de MainWindow.xaml. <Application x:Class="Test.Example.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:viewModels="clr-namespace:Test.Example.ViewModels.Designer" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:primitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Layout.Toolkit" Startup="App_OnStartup"> <Application.Resources> <ResourceDictionary> . . . </ResourceDictionary> </Application.Resources> </Application> Aquí vemos que hemos definido "App_OnStartup", un método antes que la instancia directa de la pagina principal. Así recae directamente a dicho método la responsabilidad de instanciar todos los elementos necesarios para un arranque acorde del Programa. Esto nos da una flexibilidad muy grande a la hora de construir una App. public partial class App : Application { private void App_OnStartup(object sender, StartupEventArgs e) { var bootstrapper = new Bootstrapper(); bootstrapper.Run(); } } Lo que podemos ver en la Bootstrapper clase instanciamos MainWindow (Ventana principal). En su DataContext (Visto en los tutoriales de MVVM) pasamos un ViewModel que en su constructor lleva el container creado por Unity. MainViewModel, es una ViewModel que ofrece información  a la MainView (Ventana principal). Si quisiéramos utilizar utilizar una clase que esta registrada en el contenedor de Unity en MainViewModel se haría de la siguiente forma: IProjectExplorer explorer = container.Resolve<IProjectExplorer>(); explorer.CreateNewProject(pi, persistenService); En el ejemplo podemos observar que todo pasa por el Interface y no sobre los objetos en sí. Esto se ve atractivo, más si tenemos mucho objetos de la misma Interface. Si quisiéramos cambiarlo, solo tendríamos que cambar al principio cuando registrábamos las clases en el contenedor por otro objeto. Esto se utiliza mucho para llevar los test unitarios.

Autofac

Autofac es uno de los más utilizados en el .NET. Es famoso por su rendimiento. También porque solo necesita una referencia dinámica (.dll) que depende nuestro proyecto, para Unity son dos. Para utilizar Autofac es un poco distinto a la hora programar pero el principio es exactamente el mismo. public partial class App : Application { private IContainer container; private void App_OnStartup(object sender, StartupEventArgs e) { var builder = new ContainerBuilder(); builder.RegisterType<MainViewModel>(); container = builder.Build(); } } Definimos un IContainer, luego un ContainerBuilder donde registramos las clases. Y finalmente, ContainerBuilder tiene un método Build, que la igualamos a container. Hasta aquí el tutorial! Hasta el siguiente! :D