Arena - ApplicationContext

Introducción: cómo referenciar objetos globales

Al construir una aplicación es frecuente trabajar con objetos a los que nos resultaría cómodo accederlos desde cualquier contexto:
El patrón Singleton nos permite ofrecer un único punto de acceso para este tipo de objetos desde el ambiente. No obstante, los singletons tienen como desventaja que fijan el nombre de la clase desde todos los lugares en donde se los utiliza. Supongamos que tenemos un application model, que conoce un repositorio de Ventas como un singleton, y tenemos el siguiente método que permite agregar un nuevo producto al repositorio:

def crearProducto() {
    RepoVentas.instance.agregarProducto(new Producto => [
        descripcion = descripcionDeProducto
    ])

Cuando generemos el test para ese application model sobre la funcionalidad crearProducto, estamos obligados a utilizar la implementación del repositorio de ventas (no es posible mockear el repositorio, y en general es el problema que tienen los singletons).

El siguiente gráfico muestra cómo es la relación de los objetos:


Application Context

Arena trae un objeto ApplicationContext que resuelve el inconveniente que comentamos anteriormente. Para eso trabaja con la técnica del Service Locator (que surge como una técnica para la Inyección de dependencias, el lector interesado puede profundizar su estudio en este apunte). 

La interfaz que publica es la siguiente:


Recomendamos utilizar el par de métodos
  • configureSingleton
  • getSingleton 
que se utilizan para almacenar y recuperar respectivamente objetos singleton.

Uso

Al comenzar la aplicación, debemos crear los repositorios y hacer que el application context los referencie. ¿Dónde configurar los repositorios?

1) Una opción es que lo haga la *Application cuando crea la ventana principal

// Xtend - *Application o MainWindow. En el ejemplo de los celulares es CelularApplication
override protected Window<?> createMainWindow() {
    ApplicationContext.instance.configureSingleton(typeof(Modelo), RepoModelos.instance)
    ApplicationContext.instance.configureSingleton(typeof(Celular), RepoCelulares.instance)
    return new BuscarCelularesWindow(this)
}

// Java
@Override
protected Window<?> createMainWindow() {
    ApplicationContext.getInstance()
              .configureSingleton(ModeloCelular.class, RepositorioModelos.getInstance());
    ApplicationContext.getInstance()
              .configureSingleton(Celular.class, RepositorioCelulares.getInstance());      
    return new BuscarCelularesWindow(this);
}

2) Otra opción es que haya un Bootstrap que se encargue de generar el juego de datos. Entonces podría ser que en el constructor se inicialicen los repositorios necesarios.

// Xtend
class CelularesBootstrap extends CollectionBasedBootstrap {
    new() {
       ApplicationContext.instance => [
           configureSingleton(typeof(Modelo), RepoModelos.instance)
           configureSingleton(typeof(Celular), RepoCelulares.instance)
       ]
    }

Por su parte, el test puede configurar en un método de inicialización un repositorio mockeado:

// Java
>>TestBuscadorCelular
@Before
@Override 
public void init() {
    ...
    ApplicationContext.getInstance().configureSingleton(Celular.class, new MockRepoCelulares());

// Xtend
>>TestBuscadorCelular
@Before
override init() {
    ...
    ApplicationContext.instance.configureSingleton(typeof(Celular), new MockRepoCelulares)

El Application Model obtiene el home a partir del singleton Application Context:

// Java
>>BuscadorCelular
public RepositorioCelulares getRepositorioCelulares() {     
    return ApplicationContext.getInstance().getSingleton(Celular.class);
}

public void search() {
    this.resultados = getRepositorioCelulares().search(this.numero, this.nombre);
}

// Xtend
>>BuscadorCelular
def RepoCelulares getRepoCelulares() {
    ApplicationContext.instance.getSingleton(typeof(Celular))
}

def void search() { 
    resultados = repoCelulares.search(example.numero, example.nombre)
}

Esto permite que el objeto application model trabaje con un repositorio sin saber si es el real (para la aplicación) o un impostor / mock (para el test).

A continuación vemos el gráfico que explica cómo quedan las referencias:



Comments