Simple Singleton

Existe uma discussão antiga sobre quais os pontos negativos de se utilizar Singleton em seus projetos – a maioria das pessoas argumenta que o padrão impede a realização de testes unitários nas classes que fazem uso do mesmo.

Neste post, demonstro como esta afirmação é falsa e apresento uma classe simples de criação de Singletons que permite desvios que podem ser utilizados tanto para testes quanto para substituir um framework de injeção de dependência – leia também este post.

O código

Abaixo o código da classe que gerencia a instância de um dado objeto que queremos expor como singleton. (Você precisará desta outra classe para compilar: SimpleLocker]

public sealed class SingletonFor<TheType>
{
    private static Func<TheType> _instanceBuilder = null;
    private Func<TheType> _constructorCall = null;
    private SimpleLocker _locker = null;
    private bool _isInitialized = false;
    private TheType _instance;

    public bool IsInitialized { get { return _locker.Read(() => _isInitialized); } }
    public bool UnprotectedIsInitialized { get { return _isInitialized; } }

    public SingletonFor(Func<TheType> constructorCall)
        : this(constructorCall, 1000) { }

    public SingletonFor(Func<TheType> constructorCall, int contructorLockTimeout)
    {
        if (_instanceBuilder == null) { _instanceBuilder = BuildInstance; }
        if (constructorCall == null) { throw new ArgumentNullException("constructorCall"); }
        _constructorCall = constructorCall;
        _locker = new SimpleLocker(contructorLockTimeout);
    }

    /// <summary>
    /// Changes the global builder for a given type
    ///     <remarks>This method should NEVER be used in a production code. It is intended only for creating detours for Unity Tests routines.</remarks>
    /// </summary>
    /// <param name="newGlobalBuilder">The builder body that is to be used globaly whenever an instance of this types is requested.</param>
    [Obsolete("This method should NEVER be used in a production code. It is intended only for creating detours for Unity Tests routines.")]
    public static void SetBuilderAs(Func<TheType> newGlobalBuilder)
    {
        _instanceBuilder = newGlobalBuilder;
    }

    public TheType GetInstance()
    {
        return _instanceBuilder();
    }

    private TheType BuildInstance()
    {
        if (!_locker.UpgradeableRead(() => this.UnprotectedIsInitialized))
        {
            _locker.WriteVoid(() =>
            {
                if (!this.UnprotectedIsInitialized)
                {
                    _instance = _constructorCall();
                    _isInitialized = true;
                }
            });
        }
        return this._instance;
    }
}

Um simples exemplo de como utilizar a classe de dentro de um objeto que será exposto como singleton:

public sealed class MyClass
{
    private static SingletonFor<MyClass> _singleton = new SingletonFor<MyClass>(() => new MyClass());

    public static MyClass Instance { get { return _singleton.GetInstance(); } }

    private MyClass() { }

    public void DoSomething() { }
}

Ok, isto resolve o problema do singleton. Nada de mais até aqui, certo?

Para que possamos mocar esta chamada, precisamos definir uma abstração que nos permita substituir a instância desta classe por qualquer outro objeto que respeite o contrato sendo exposto. Vamos pelo caminho da interface para resolver isto.

Vamos extrair o contrato atual desta classe para a seguinte interface:

public interface IDoSomething
{
    void DoSomething();
}

Deixando a classe da seguinte forma:

public sealed class MyClass : IDoSomething
{
    private static SingletonFor<IDoSomething> _singleton = new SingletonFor<IDoSomething>(() => new MyClass());

    public static IDoSomething Instance { get { return _singleton.GetInstance(); } }

    private MyClass() { }

    public void DoSomething() { }
}

Nada de mais, certo? Apenas abstraindo para interface. Nenhuma magia negra necessária.

Por incrível que pareça, isto é tudo o que precisamos para realizar desvios em classes que dependam deste objeto. Para provar isto, vamos imaginar que esta classe é consumida pelo seguinte objeto:

public sealed class MyOtherClass
{
    public int SumAndDoSomething(int x, int y)
    {
        MyClass.Instance.DoSomething();
        return x + y;
    }
}

Ok, concordamos que não estamos preocupados com SRP aqui.

Supondo que precisamos testar apenas a parte da soma ignorando completamente os possíveis efeitos colaterais que a chamada para “MyClass.Instance.DoSomething” possa acrescentar ao método (IO/Acesso a banco/Envio de email/etc), podemos simplesmente escrever um teste da seguinte forma.

[TestMethod]
public void Sum_Without_SideEffects()
{
    SingletonFor<IDoSomething>.SetBuilderAs(() => new MyClassMock());
    var testClassInstance = new MyOtherClass();
    Assert.AreEqual(testClassInstance.SumAndDoSomething(1, 2), 3);
}

public class MyClassMock : IDoSomething
{
    public void DoSomething() { }
}

Simples, concorda?

:)

Um comentário

  1. Pingback: Injeção de dependência minimalista | Felipe G Te . com

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: