2009-02-25 12 views
6

Me gustaría forzar a las subclases a implementar el patrón singleton.Forzar patrón único en las subclases

Originalmente pensé en tener una propiedad estática abstracta en la clase principal, pero desde un punto de vista más cercano, eso no tenía sentido (el resumen requiere una instancia).

A continuación, pensé en tener una interfaz con una propiedad estática, pero eso tampoco tiene sentido (las interfaces también requieren una instancia).

¿Es esto algo que es posible, o debería renunciar a esta línea de pensamiento e implementar una fábrica abstracta?

+0

He estado en la valla con respecto a los singletons durante algún tiempo. Pero este hilo hace algunos buenos puntos para no usarlos: http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons – JMD

+0

Hay cientos de hilos, publicaciones en blogs y artículos llenos de razones que no para usar singletons Es extremadamente raro que realmente mejoren tu código. Por favor reconsidera esto. Usted, y cualquier otro desarrollador que trabaje con ese código, se arrepentirá de incluir singleton en todas partes. – jalf

Respuesta

6

Por favor reconsidere. NO quieres usar singletons aquí. Está poniendo a disposición algunas funcionalidades para los usuarios que derivan de su clase. Esta bien. Pero también estás dictando una forma específica en la que siempre debe usarse, y sin ninguna razón. Eso no es bueno.

Puede tener sentido solo crear una instancia de un objeto de esta clase la mayoría de las veces, pero en ese caso, simplemente ejemplifique el objeto una vez. No es muy probable que crearas una instancia de una docena de objetos accidentalmente sin darte cuenta. Además, ¿cómo puede saber que tener dos instancias será NUNCA útil? Puedo pensar en varios casos incluso ahora.

Pruebas unitarias: es posible que desee que cada prueba cree una instancia de este objeto y vuelva a derribarlo. Y como la mayoría de las personas tiene más de una prueba de unidad, necesitará crear una instancia más de una vez.

O tal vez en algún momento decidas tener varios niveles idénticos/similares en tu juego, lo que significa crear instancias múltiples.

un producto único le da dos cosas:

  • Una garantía de que no más de una instancia del objeto siempre se creará una instancia, y
  • Acceso global a dicha instancia

Si No necesito estas dos cosas, hay mejores alternativas. Ciertamente no necesita acceso global. (los globales son malos, y generalmente son un síntoma de un mal diseño, especialmente en datos variables como el estado de tu juego)

Pero no necesitas la garantía de que tampoco se crearán instancias de más de una instancia. ¿Es el fin del mundo si instancia el objeto dos veces? ¿Se bloqueará la aplicación? Si es así, necesitas esa garantía. Pero en su caso, no pasará nada malo. La persona que instancia el objeto simplemente usa más memoria de la necesaria. Pero él podría tener una razón.

Simplemente ponga en la documentación de la clase que esta es una clase muy grande y costosa, y no debe instanciarla más a menudo de lo necesario. Problema resuelto. No elimina la flexibilidad que podría resultar útil más adelante, no otorga acceso global a los datos sin ningún motivo. Como puede controlar quién puede ver el objeto, no necesita ahogarlo en bloqueos que se convertirán en un cuello de botella en aplicaciones multiproceso. No tiene dependencias ocultas diseminadas por su código, lo que hace que sea más difícil de probar y más difícil de reutilizar.

4

Intente utilizar un contenedor IOC. La mayoría de los buenos contenedores de COI permiten el uso del patrón de singleton sin tener que implementarlo usted mismo (es decir, el marco de primavera): me gusta más que forzar un método estático GetInstance().

Además, no es posible en Java, sin embargo, funcionaría en C++ con plantillas.

+0

Eso se prestaría muy bien al diseño general. Además, esto no es para Java, sino para C#. –

+0

Spring tiene un contenedor .NET IOC. –

+0

También podría intentar rodar su propio contenedor de IoC si no necesita algo complicado. Hay un buen podcast dnrTv sobre cómo construir uno aquí: http://www.dnrtv.com/default.aspx?showNum=126 – Joseph

0

Yo definiría un sellado clase que obtiene su funcionalidad de los delegados pasados ​​al constructor, algo como esto:

public sealed class Shape { 
    private readonly Func<int> areaFunction; 

    public Shape(Func<int> areaFunction) { this.areaFunction = areaFunction; } 

    public int Area { get { return areaFunction(); } } 
} 

Este ejemplo, no tiene mucho sentido, sólo se ilustra una patrón. Tal patrón no se puede usar en todas partes, pero a veces ayuda.

Además, se puede ampliar para exponer un número finito de campos estáticos:

public sealed class Shape { 
    private readonly Func<int> areaFunction; 

    private Shape(Func<int> areaFunction) { this.areaFunction = areaFunction; } 

    public int Area { get { return areaFunction(); } } 

    public static readonly Shape Rectangle = new Shape(() => 2 * 3); 
    public static readonly Shape Circle = new Shape(() => Math.Pi * 3 * 3); 
} 
+0

El objetivo de tener la llamada estática para devolver la instancia es para que los constructores no estén disponibles y creen instancias fuera de la clase es imposible. Tu solución no implementa el patrón singleton. –

+0

Una clase estática es siempre un singleton (hay pocos escenarios, donde no se puede usar la clase estática como singleton). En esta solución hay campos/propiedades estáticas que producen el mismo resultado. –

1

¿Por qué? Si alguien quiere usar varias instancias de una subclase de su clase, podría tener una razón perfectamente válida para hacerlo.

Si desea hacer algo que solo se debe hacer una vez para cada clase que subclasifica su clase (por qué, no tengo ni idea, pero puede tener una razón para hacerlo), use un diccionario en la clase base.

+0

Las clases en cuestión son objetos bastante grandes que representan una escena en un juego 2D.No quiero volver a crear estas escenas si se crearon antes y quiero que la información de estado de cada escena permanezca mientras no esté en uso. –

+0

Debe editar su pregunta y agregar esto al texto. Creo que influirá en las respuestas que dan las personas. –

+0

En ese caso, desea que la clase sea tanto sellada como simple. También podría considerar colocar la escena en una clase separada, que es única y sellada, y luego soltar el requisito de singleton en la clase original. – erikkallen

0

Creo que será mejor con un patrón de fábrica aquí para ser honesto. O use una herramienta de IoC como recomienda Brian Dilley. En el mundo C# hay cargas, aquí están las más populares: Castle/windsor, StructureMap, Unity, Ninject.

Dejando eso de lado, ¡pensé que sería divertido intentar solucionar tu problema! Echar un vistazo a esto:

//abstract, no one can create me 
public abstract class Room 
{ 
    protected static List<Room> createdRooms = new List<Room>(); 
    private static List<Type> createdTypes = new List<Type>(); 

    //bass class ctor will throw an exception if the type is already created 
    protected Room(Type RoomCreated) 
    { 
     //confirm this type has not been created already 
     if (createdTypes.Exists(x => x == RoomCreated)) 
      throw new Exception("Can't create another type of " + RoomCreated.Name); 
     createdTypes.Add(RoomCreated); 
    } 

    //returns a room if a room of its type is already created 
    protected static T GetAlreadyCreatedRoom<T>() where T : Room 
    { 
     return createdRooms.Find(x => x.GetType() == typeof (T)) as T; 
    } 
} 

public class WickedRoom : Room 
{ 
    //private ctor, no-one can create me, but me! 
    private WickedRoom() 
     : base(typeof(WickedRoom)) //forced to call down to the abstract ctor 
    { 

    } 

    public static WickedRoom GetWickedRoom() 
    { 
     WickedRoom result = GetAlreadyCreatedRoom<WickedRoom>(); 

     if (result == null) 
     { 
      //create a room, and store 
      result = new WickedRoom(); 
      createdRooms.Add(result); 
     } 

     return result; 
    } 
} 

public class NaughtyRoom :Room 
{ 
    //allows direct creation but forced to call down anyway 
    public NaughtyRoom() : base(typeof(NaughtyRoom)) 
    { 

    } 
} 

internal class Program 
{ 
    private static void Main(string[] args) 
    { 
     //Can't do this as wont compile 
     //WickedRoom room = new WickedRoom(); 

     //have to use the factory method: 
     WickedRoom room1 = WickedRoom.GetWickedRoom(); 
     WickedRoom room2 = WickedRoom.GetWickedRoom(); 

     //actually the same room 
     Debug.Assert(room1 == room2); 

     NaughtyRoom room3 = new NaughtyRoom(); //Allowed, just this once! 
     NaughtyRoom room4 = new NaughtyRoom(); //exception, can't create another 
    } 
} 

WickedRoom es una clase que implementa correctamente el sistema. Cualquier código de cliente obtendrá la clase WickedRoom de singleton. NaughtyRoom no implementa el sistema correctamente, pero incluso esta clase no se puede instanciar dos veces. Una segunda instanciación da como resultado una excepción.

+0

Eso es muy similar a mi implementación actual. Mi problema es que nada impide que NaughtyRoom o WickedRoom no implementen sus métodos Get. Quiero forzar a las subclases a implementar esos métodos Get. –

0

Aunque esto no obligará al usuario a tener una subclase singleton, puede forzar al usuario a crear solo una instancia de la clase (o sus subclases) como se muestra a continuación. Esto generará un error si se crea una segunda instancia de cualquier subclase.

public abstract class SuperClass { 
private static SuperClass superClassInst = null; 

public SuperClass() { 
    if(superClassInst == null) { 
     superClassInst = this; 
    } 
    else { 
     throw new Error("You can only create one instance of this SuperClass or its sub-classes"); 
    } 
} 

public final static SuperClass getInstance() { 
    return superClassInst; 
} 

public abstract int Method1(); 
public abstract void Method2(); 
} 
Cuestiones relacionadas