martes, 4 de septiembre de 2007

[Patterns] Lifetime Container Pattern

Muchas veces es necesario mantener el control del tiempo de vida de los objetos. En tecnologias de código administrado como .Net o Java, a diferencia de otros como C y C++, existe un mecanismo de recolección de basura que libera al programador de la tarea de destrucción de objetos y la liberación de la memoria. No obstante, existen ocaciones en las que tener el control de la destrucción de los objetos es muy conveniente.

Por ejemplo, cuando la creación de un objeto consume mucho tiempo y recursos del procesador, crear estos objetos y permitir que se destruyan cuando se pierden las referencia a él puede que no sea una buena idea, en este caso se opta por crear un pool de objetos que no se destruyan cuando ya no se usen sino que permanezan disponibles aún cuando no se los esté usando. Otra situación se da cuando se necesita liberar un conjunto de recursos (objetos) que ya no se van a utilizar más porque en su conjunto representan o sirven a una sola unidad de trabajo. Un caso concreto es cuando se utilizan objetos que administran gran cantidad de otros objetos también completos como los WorkItem del Composite UI Appication Block el cual mantiene colecciones de objetos Views, Controlers, Commands, EventTopics, Services, SmartParts, UIExtensionSites, WorkSpaces, otros WorkItems y algunas cosas más. En este caso, cuando se libera un workitem, se deben liberar todos los objetos que han colaborado con él.

El Lifetime container, como su nombre lo indica, es un contenedor (una colección) que mantiene vivas las referencias a todos los objetos que se registran en él. Vamos al código:

using System;
using System.Collections;
using System.Collections.Generic;

namespace Temosoft.Containers
{
public class LifetimeContainer : IEnumerable<object>, IDisposable
{
#region Private Fields
// El contenedor interno que mantiene vivas las referencias.
private List<object> items = new List<object>();
#endregion

#region Public Methods (Operations)
public void Add(object item)
{
items.Add(item);
}

public void Remove(object item)
{
if (!items.Contains(item))
return;

items.Remove(item);
}

public bool Contains(object item)
{
return items.Contains(item);
}
#endregion

#region Public Properties (Read only)
public int Count
{
get { return items.Count; }
}
#endregion


#region IDisposable interface
public void Dispose()
{
Dispose(true);
}

protected void Dispose(bool disposing)
{
if (disposing)
{
List<object> itemsCopy = new List<object>(items);
itemsCopy.Reverse();

foreach (object o in itemsCopy)
{
IDisposable d = o as IDisposable;

if (d != null)
d.Dispose();
}

items.Clear();
}
}
#endregion


#region Enumerators
public IEnumerator<object> GetEnumerator()
{
return items.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
}

Como se puede ver, esta clase es muy parecida a una colección salvo porque implementa IDisposable para manejar la liberación de los objetos en orden inverso a su registración. No implementa ICollection porque en este caso no se debe posibilitar el método CopyTo() y porque en este caso, por razones de sencillez, no se hizo thread safe.

Como se ve, este es un contenedor sumamente sencillo y surge la pregunta "que hay de nuevo acá?", bueno, la verdad es que lo nuevo lo encontramos en la manera o las formas de usarlo. Veamos, en la entrada de este blogs: [Patterns] Distributed Applications using FactoryMethod and ServiceLocator Patterns, vimos como la clase ServiceLocator mantenia un diccionario de strings y objetos (después lo hicimos mediante object-object), este diccionario impide que los servicios que no se utilizan puedan ser eliminados! Como podemos manejar esto con un LifetimeContainer? la respuesta es NO mantener las referencias. Esto lo podemos hacer implementando el ServiceLocator mediante un weakRefDictionary<object, object> en lugar de un Dictionary<object, object>.

public class ServiceLocator
{
private WeakRefDictionary<object, object> services = new WeakRefDictionary<object, object>();

public void AddService(object serviceKey, object service)

Que beneficios nos trae esto? Ahora, cada componente, plugin, unit of work, form, o lo que sea puede registrar sus servicios, de manera que estén disponibles para todas los demas componentes, mientras dure su vida y que dejen de estarlo cuando ellos son destruidos (a menos que alguien los esté usando). Por lo tanto, cada componente tiene su propio Lifetime container para manejar el ciclo de vida de SUS objetos.

Lucas Ontivero

No hay comentarios: