En este artículo voy a explicar que es un patrón Singleton, como se crea, para qué sirve, y como combinarlo con la técnica de Lazy Loading incluida en .NET para que sea aún más elegante.

¿Te parece poco? Pues también te daré un proyecto para que lo veas, lo pruebes y juegues con él.

Así que no perdamos más tiempo… y pongámonos manos a la obra!

Singleton

¿Que es un Singleton?

El patrón de diseño singleton es el primer patrón que le viene a cualquier programador a la cabeza. Es de los más sencillos y el más preguntado en todas las entrevistas de trabajo para desarrollador del mundo 😉

Consiste en restringir una clase a tener una única instancia, en vez de generar una nueva instancia cada vez que se necesita en un proceso. Esto es útil en situaciones donde la clase se utiliza para coordinar varios procesos o provee utilidad general a toda nuestra aplicación.

Antes de aplicar este patrón deberemos tener en cuenta que la instancia que creamos se mantiene en memoria durante todo el proceso, o la vida de la aplicación, con lo cual siempre hay que contar con el consumo de recursos de la clase a la hora de aplicar el patrón singleton o elegir otra solución.

Creando nuestro Singleton

El patrón de código habitual para generar una clase singleton sería algo parecido a esto:

public class Repo
{
    private static readonly Repo _instance = new Repo();
    public static Repo Instance  { get { return _instance; } }

    private IEnumerable<Country> _countries; 

    private Repo() 
    { 
        Init(); 
    } 

    private void Init() {
        var remoteStorage = new MyWebService(); 
        _countries = remoteStorage.GetCountries();     
    }

    public IEnumerable<Country> GetCountries() 
    {
        return _countries;
    }
}

En el ejemplo se ven algunas de las características habituales de una clase Singleton:

  • La clase declara un campo privado de su mismo tipo (Repo). En ese campo es donde se encapsulará la instancia única de nuestro objeto Singleton.
  • Este objeto encapsulado, normalmente se expone mediante una propiedad pública a la que se le suelen dar nombres como Instance, Current o incluso Singleton.
  • El constructor suele ser privado, para evitar que se instancie la clase manualmente con un new, forzando a usar la nuestra instancia.
  • Para modelos de aplicación poco complicados, podemos usar campos de tipo static, aunque también se puede hacer mediante inyección de dependencias controlando el ciclo de vida del objeto (sesión, hilo, aplicación, etc…)

En nuestro ejemplo hemos llamado Instance a la propiedad pública. A la hora de utilizar nuestro objeto Singleton, utilizaríamos el siguiente código:

var countries = Repo.Current.GetCountries();

Esto contrasta con la manera habitual de un objeto “no singleton” que sería:

var countries = new Repo().GetCountries();

Utilidad del Singleton

Una clase diseñada con el patrón singleton nos da la ventaja de que no se instancia la clase cada vez que se utiliza, sino que se instancia la primera vez, y se reutiliza en subsecuentes llamadas. Vamos a seguir con el ejemplo anterior. Nuestro método se encarga de obtener las ciudades que comienzan por la letra que le pasemos…

private IEnumerable<Country> GetCountriesWithLetter(string letter)
{
    var countries = new Repo().GetCountries()
         .Where(o => o.Name.UpperCase().StartsWith(letter.UpperCase()));
}

… y más adelante en la aplicación tenemos esto:

AllMyCustomers.ForEach(c => c.CountriesWithYourInitial = 
                     GetCountriesWithLetter(c.Name.Substring(0,1)));

Este trozo de código tomaría todos los clientes de la lista AllMyCustomers y cargaría en su propiedad CountriesWithYourInitial los países que comenzasen por la inicial de su nombre. (Es una tontería, lo sé, pero tenía que hacer algo de ejemplo… 😉 )

Ahora imaginemos que tengo cientos, o miles de clientes. Como podéis ver, el constructor de la clase Repo usa el método Init que se encarga de conectar a un WebService y traerse todos los clientes para guardarlos en una propiedad privada. Si hacemos eso miles de veces, nuestra aplicación va a morir… o directamente tardara horas en hacer el trabajo.

Aquí es donde el patrón Singleton viene al rescate… si el método GetCountriesWithLetter() utiliza un repositorio Singleton, de esta manera:

private IEnumerable<Country> GetCountriesWithLetter(string letter)
{
    var countries = Repo.Current.GetCountries()
             .Where(o => o.Name.UpperCase().StartsWith(letter.UpperCase()));
}

Solamente se cargarán los paises en nuestra clase Repo la primera vez que la usemos, reutilizando _instance todas las demás llamadas, y evitando llamadas múltiples a nuestro WebService.

Lazy Loading

El Lazy Loading es una técnica muy utilizada en la programación. Su uso es muy recomendable cuando tenemos objetos que contienen propiedades que son costosos de inicializar a nivel de recursos (tiempo, memoria, etc…) y que no sabemos si se van a utilizar en todos los casos.

Veamos este ejemplo:

public class Customer
{
    public string Code { get; set; }
    public string Name { get; set; }
    public IQueryable<Invoice> InvoiceList { get; set; }

    public static Customer Get(string code)
    {
        var cust = Repo.GetCustomer(code);
        cust.InvoiceList = Repo.GetCustomerInvoices(cust.Code);
        return cust;
    }
}

Como se puede ver, cuando buscamos un cliente por su código, se obtiene del repositorio, y además se obtienen sus facturas. El proceso de obtener las facturas podría ser costoso debido a que es una operación que podría afectar a miles de registros de la base de datos o un webservice, y a lo mejor solo queremos hacer algo trivial como esto:

var cust = Customer.Get(code);
Console.WriteLine(string.Format("Hello there, {0}!", cust.Name));

¡Resulta que hemos obtenido todas las facturas del cliente solo para decirle hola!

Aquí es donde se impone la técnica de Lazy Loading. Vamos a cambiar la clase anterior para que acabe así:

public class Customer
{
    public string Code { get; set; }
    public string Name { get; set; }

    private IQueryable<Invoice> _invoiceList;
    public IQueryable<Invoice> InvoiceList
    {
        get
        {
            if (null == _invoiceList)
                _invoiceList = Repo.GetCustomerInvoices(this.Code);
            return _invoiceList;
        }
    }

    public static Customer Get(string code)
    {
        return Repo.GetCustomer(code);
    }
}

Hemos cambiado la responsabilidad de cargar las facturas del cliente a la propiedad InvoiceList en sí. De esta manera, cuando se accede a la propiedad, ésta comprueba si ya se ha cargado antes (no sería null), y si no, se cargaría en ese momento.

Ahora sí podemos cargar un cliente y mostrar su nombre sin cargar las facturas, pero seguiríamos teniendo acceso a ellas en cualquier momento:

// Aun no hemos cargado facturas... nadie las ha mirado
var cust = new Customer().Get(code);
Console.WriteLine(string.Format("Hello there, {0}!", cust.Name);

// Ahora al acceder a InvoiceList se cargarían las facturas...
Console.WriteLine(string.Format("   You have {0} invoices!", 
                      cust.InvoiceList.Count());

Añadiendo elegancia con Lazy<T>

Esto se lleva haciendo desde los siglos de la prehistoria de la programación orientada a objetos, así que Microsoft, en la version 4 de .NET, decidió facilitar un poco el tema a los programadores, implementando la clase genérica Lazy<T>

Mediante esta clase nuestro código queda mucho más elegante, aunque el resultado es el mismo. Aplicado a la clase anterior…

    ...

    private Lazy<IQueryable<Invoice>> _invoiceList =
        new Lazy<IQueryable<Invoice>>(() => Repo.GetCustomerInvoices(this.Code);
    public IQueryable InvoiceList
    {
        get { return _invoiceList.Value; }
    }

    ....

Esto hace exactamente lo mismo que nuestro ejemplo anterior, pero la clase Lazy se encarga de lanzar el delegado para inicializar el valor cuando accedemos a ella. Lo dicho, más bonito.

Bien … vamos a ver como podemos aplicar esto a la clase singleton que hemos visto antes…

Uniendo el trabajo

Hemos visto que es y como se hace un singleton, y también como aplicar la técnica de Lazy Loading a una propiedad.

El patrón singleton es un buen sitio para aplicar esta técnica, ya que tiene su instancia que solamente se genera una vez, y muchas veces es un objeto que consume recursos y no se va a utilizar en todas nuestras rutas de código.

Veamos como quedaría nuestra clase singleton si le aplicamos la técnica de Lazy Loading:

public class Repo
{

    private static readonly Lazy<Repo> _instance = new Lazy<Repo>();
    public static Repo Instance  { get { return _instance.Value; } }

    private Lazy<IEnumerable<Country>> _countries =
           new Lazy<IEnumerable<Country>>(() => new MyWebService().GetCountries());

    // A partir de aqui, ya los métodos normales de la clase (no estáticos)

    public IEnumerable<Country> GetCountries()
    {
        return _countries.Value;
    }
}

Elegante, ¿verdad? Esto añade muchos enteros en cuanto a legibilidad y elegancia en tus clases, así que no dudes en usarlo!

Proyecto demo

He preparado un pequeño proyecto de ejemplo con singletons, lazy loading y Lazy<T> para que veáis como funciona paso a paso, experimentéis y hagáis vuestras pruebas…

Puedes clonarlo con Git desde esta dirección:

https://bitbucket.org/joanvilarino/lazysingletondemo.git

O simplemente descargarlo desde esta página:

http://repo.joanvilarino.info/lazysingletondemo/downloads

Gracias por tu tiempo!!!

Follow me

Joan Vilariño

Senior .NET Developer at Ohpen
Follow me