El enésimo post sobre el Singleton (UPDATE 1)

La cosa es que el Singleton hace dos cosas:

- Se asegura de que sólo haya una instancia del objeto.

- SomeStuff()

El problema de hacer dos cosas es que viola la S de los principios Solid: Single Responisability Principle. Y esto hace que no sea fácilmente testeable.

Cuando quiero hacer que sea testeable quito el Singleton y lo sustituyo por una factoría, una clase estática y una clase que hace la funcionalidad de SomeStuff.

Aquí tenéis un ejemplo simple:

public static class Singletons
 {
     private static Factoria fact = new Factoria();

     public static void HazLoTuyo()
     {
         fact.GetUnicaInstancia().DoStuff();
     }
 }

 public class Factoria
 {
     private Clase unicaInstancia;

     public Factoria()
     {
         unicaInstancia = new Clase();
     }

     public Clase GetUnicaInstancia()
     {
         return this.unicaInstancia;
     }
 }

 public class Clase
 {
     public Clase()
     {
     }

     public void DoStuff() { }
 }

De esta manera puedo testear la clase Clase sin preocuparme de que sólo hay una instancia de ella en el sistema.

Es por esto que me asalta la duda, ¿no será el Singleton un antipatron?

¿Cómo lo veis vosotros?

[Update 1]

Gracias a los comentarios voy a ir actualizando el post con algunas conclusiones y dudas que se me plantean.

Resumiendo un poco los comentarios, cuando sólo queremos una instancia de nuestra clase Clase (definida más arriba) suele ser buena idea usar un inyector de dependencias y registrar nuestra clase para que el inyector nos devuelva siempre la misma instancia.

De esta manera "invertimos el control" de la instanciación de nuestra clase, y podemos testearla de manera aislada.

¿Pero que pasa si no queremos meter un inyector de dependencias por el motivo que sea?

Podríamos meter una clase parecida a esta:

 public class ClaseFachada
 {
     private static Clase clase = new Clase();

     public static void DoStuff()
     {
         clase.DoStuff();
     }
 }

De esta manera podemos usarla en nuestro código de esta forma:

ClaseFachada.DoStuff();

Como si fuese un Singleton, pero evitando el típico Clase.Instance.DoStuff(). La cosa es que si estamos desarrollando una librería y queremos que nuestra librería se use así.

¿Os parece adecuado? ¿Cómo lo mejoraríais?

9 pensamientos en “El enésimo post sobre el Singleton (UPDATE 1)

  1. Javier J.

    Sí, la mayoría de las veces es un antipatrón 😉 Todo lo que dependa de algo estático y a la vez tenga estado suele ser una mala decisión porque introduce unas dependencias muy fuertes.

    Aunque este sea el enésimo post, creo que nunca hay suficientes, sobre todo cuando se cuentan las cosas tan claramente y tan bien.

  2. Luis

    Para mí está claro, si necesito crear un “singleton” aplico “factory method”.

  3. miguel

    Pero para que necesitas crear una clase estatica? Con Singleton deberias crear un objecto estatico de una clase no-estatica. Ahi esta la gracia no?

    1. juanlao Autor

      Hola Miguel:
      El motivo de la clase estática es para tener una “fachada” con la que acceder a la única instancia del objeto.
      El objetivo es poder usar el código de la siguiente manera:
      Singletons.HazLoTuyo()

      Usando un Singleton clásico se usaría con el típico
      Clase.Instance.DoStuff();

      ¿Se te ocurre otra opción de conseguirlo o conoces otra opción mejor?

      ¡Gracias por el comentario!

  4. soywiz

    Los singletons usados de forma estática pueden llegar a ser igual de malos que estáticos en ciertos casos. Si se usa un sistema de inyección de dependencias, es el inyector el que proporciona los singletons con lo que no se viola el principio de responsabilidad única. Además en cada test podemos crear un contexto de inyección de nuevo y mockear las clases/instancias que se le van a pasar a nuestra clase. Y durante la creación de nuestro objeto no necesitamos acoplarnos a una factoría estática. Podemos pedir una interfaz y que el que configuró el inyector se encargue de decidir cuál es la implementación adecuada.

  5. Javier J.

    soywiz lo explica muy bien.

    Juanma, tienes que cambiar la dirección. En vez de ser tú quién pidas las instancia de un objeto debes dejar que sean otros quien te den las instancias que debes usar. Tu clase simplemente expone los contratos que necesita y deja que otros le proporcionen las clases, sean cuales sean, que usen esos contratos.

    Si lo haces así podrás ir haciendo tu propia inversión de dependencias o empezar a utilizar herramientas.

    Un saludo.

  6. juanlao Autor

    ¡Genial, gracias por los comentarios!

    Estoy totalmente de acuerdo con lo que comentáis: Si usamos un inyector de dependencias está claro que es él el que se encarga de instanciar e indicar qué clases deben ser singletons.
    Me gustaría saber cómo lo hacéis vosotros cuando no hay un inyector de dependencias de por medio y queréis conseguir un Singleton para poder usarlo, como ya he comentado en la respuesta al comentario de Miguel:
    En vez de usarlo de esta forma:
    Clase.Instance.DoStuff();
    Poder usarlo de esta:
    Singletons.HazLoTuyo()

    ¡Espero vuestras respuestas y poder actualizar el post con todo lo que hablemos!

  7. juanlao Autor

    Esto me huele que aplicando TDD, al final, saldría una implementación de un inyector de dependencias con la funcionalidad justa para el proyecto en el que esté trabajando.
    ¿Qué pensáis?

    1. javierjus

      No tiene por qué. En mi opinión (y discrepo un poco de lo que cuentan otras personas) TDD no te da un diseño, solo te avisa de que algo va mal. Arreglarlo ya es cosa tuya.

      Creo que el valor de TDD aquí es avisarte de la dependencia estática tan burraca que metes al descubrir que no puedes probar el objeto de manera aislada porque depende de la llamada al singleton y dependes de la instancia real.

      Realmente si te acostumbras a aplicar ID (ver enlace anterior) o sueles trabajar con motores de inyección de dependencias, te ahorras este camino.

      PD: Una alternativa guarra es poder modificar lo que te devuelve el Singleton, por ejemplo bajando la visibilidad del atributo estático o usando algo como MS Fakes. Pero algo como esto solo deberías usarlo cuando no hay más alternativas.

Los comentarios están cerrados.