08/11/2023 | Tecnologías

Inyección de dependencias para AvaloniaUI con Splat

Introducción al Patrón de Inversión de Dependencias

En cualquier sistema que trabaje un diseño orientado a objetos es común la presencia de lo que se denomina “inversión de dependencias”. Este principio permite desacoplar nuestro código en módulos reutilizables a lo largo del programa. Pero, ¿qué quiere decir esto de “desacoplar el código”?

Cuando hablamos de código acoplado o nivel de acoplamiento del código, nos referimos al nivel de dependencia que tiene una funcionalidad específica de nuestro sistema con respecto a otras rutinas o módulos. Cuanto más dependiente sea esta parte del código de otras dependencias más acoplado está.

Un sistema robusto y sólido debería tener funcionalidades con un bajo acoplamiento; queremos módulos bien cohesionados, que puedan mantenerse por separado y que podamos usar en diferentes puntos del programa sin requerir gran esfuerzo o tener que hacer grandes cambios estructurales en el programa.

Es aquí cuando entra en juego el principio de inversión de dependencias; este principio trata de promover un diseño en el que los elementos de mayor y los de menor nivel de abstracción no dependan entre sí directamente. Propone abstraer la interacción entre componentes y crear un mecanismo intermediario que interactúe por medio de interfaces con cada componente sin tener en cuenta la implementación concreta de cada uno.

Inyección de Dependencias

Dentro de la aplicación del patrón de inversión de dependencias es importante la manera en la que vamos a instanciar cada componente de nuestra aplicación. Para este patrón existen dos patrones de diseño que nos permitirán instanciar un módulo siguiendo los principios que nos brinda la inversión de dependencias: el patrón de fábrica y el patrón de inyección de dependencias. En este artículo nos centraremos en el segundo.

El patrón de inyección de dependencias consiste en un mecanismo proveedor que dispondrá a nuestra aplicación de los módulos necesarios para su funcionamiento. En vez de delegar la creación de un objeto a la clase, ésta será la encargada de, por medio de clases “contenedoras” (lo que podría ser una interfaz o clase abstracta), inyectar una implementación específica a esa clase contenedora.

Este patrón sigue el principio de inversión de dependencias, el módulo receptor desconoce y es independiente de la implementación del servicio provisto. Simplemente interactúa con éste por medio de una interfaz.

¿Por qué Splat?

Uno de los principales frameworks que usamos dentro de nuestros proyectos en AvaloniaUI es ReactiveUI. Nos facilita la interacción dinámica entre el front-end y back-end, el desarrollo multiplataforma y, en este caso, la modularización de la aplicación. Para que esto sea posible, uno de los frameworks que vienen integrados en ReactiveUI. Entre otras cosas, Splat viene integrado con un mecanismo de inyección de dependencias.

Las principales ventajas que tiene Splat son su rapidez y su simpleza. A diferencia de otras herramientas de inversión de dependencias, Splat no sacrifica el uso de memoria dinámica a cambio de mayor velocidad de reacción, lo cual está bien para servidores, pero no tanto para aplicaciones de escritorio o para Smartphones.

Para conseguir esa rapidez y eficiencia de recursos utiliza lo que se conoce como Service Locator para el mecanismo de inyección de dependencias. Éste no necesita de una configuración muy compleja, su acceso a servicios es muy rápido y es capaz de manejar diferentes modelos de duración de objetos (singleton o lazy).

¿Cómo se utiliza Splat?

El uso básico es muy sencillo, se compone de dos partes: Registro y Resolución.

Podemos registrar un servicio de tres maneras diferentes, aquí veremos un ejemplo de la documentación oficial de ReactiveUI:

Locator.CurrentMutable.Register(() => new Toaster(), typeof(IToaster)); Locator.CurrentMutable.RegisterConstant(new ExtraGoodToaster(), typeof(IToaster)); Locator.CurrentMutable.RegisterLazySingleton(() => new LazyToaster(), typeof(IToaster));

Como podemos observar, cada tipo de registro responde a un modelo de duración de objeto distinto. El primer método registra un servicio de tipo “IToaster” de manera que, cada vez que se llame a un servicio de ese tipo, se crea una nueva instancia cada vez. El segundo método responde a lo que conocemos como Singleton; éste servicio se instala al principio del programa y se utilizará a lo largo del mismo. Por último, está el tercer método que, como su nombre indica, también se trata de un Singleton; en este caso tenemos que tener en cuenta la palabra “Lazy”, que contiene el nombre de este método, puesto que este singleton no será instanciado hasta su primer uso.

A continuación, un ejemplo real de registro de dependencias en una de nuestras aplicaciones de AvaloniaUI:

Locator.CurrentMutable.RegisterConstant(new WindowPopupService(), typeof(WindowPopupService));

En este caso usamos un servicio de ventanas para mensajes de información al usuario y error. Como hemos visto anteriormente, este servicio está siendo registrado como Singleton, de manera que se instancia al principio del programa y será destruido una vez éste sea cerrado por el usuario.

Es recomendable mantener todo el registro de servicios en un mismo lugar, ya sea una clase o un método a modo de bootstrapper.

Una vez hecho el registro, podremos hacer la resolución de dependencias con el método GetService. En el caso del ejemplo anterior, para acceder al servicio de Pop-Ups debemos hacer lo siguiente:

Locator.Current.GetService<WindowPopUpService>();

Y así es cómo podemos implementar un mecanismo de inyección de dependencias con Splat.

Compartir en:

Relacionados