views:

687

answers:

5

I would like to force subclasses to implement the singleton pattern.

I originally thought of having an abstract static property in the parent class, but upon closer though, that didn't make sense (abstract requires and instance).

Next, I thought of having an interface with a static property, but that also doesn't make sense (interfaces also require an instance).

Is this something which is possible, or should I give up this train of thought and implement an abstract factory?

+4  A: 

Try using an IOC container. Most good IOC containers enable the use of the singleton pattern without having to implement it yourself (ie: spring framework) - I like this better than forcing a static GetInstance() method.

Besides, it's not really possible in java, it would work in C++ with templates though.

Brian Dilley
That would lend itself very well to the overall design. Also, this isn't for Java but for C#.
Ben S
Spring has a .NET IOC container.
Brian Dilley
You could also try rolling your own IoC container if you don't need anything complicated. There's a good dnrTv podcast on how to build one here: http://www.dnrtv.com/default.aspx?showNum=126
Joseph
A: 

I would define a sealed class that gets its functionality from delegates passed to the constructor, something like this:

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

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

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

This example does not make a lot of sense, it just illustrates a pattern. Such a pattern cannot be used everywhere, but sometimes it helps.

Additionally, it can be extended to expose a finite number of static fields:

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);
}
Michael Damatov
The whole point of having the static call to return the instance is so that constructors are unavailable and creating instances outside the class is impossible. You solution does not implement he singleton pattern.
Ben S
A static class is always a singleton (there are few scenarios, where static class as singleton cannot be used). In this solution there static fields/properties that produce the same result.
Michael Damatov
+1  A: 

Why? If someone wants to use multiple instances of a subclass of your class they might have a perfectly valid reason to.

If you want to do something that only should be done once for each class that subclasses your class (why, I have no idea, but you might have a reason to), use a Dictionary in the base class.

erikkallen
The classes in question are fairly large objects which represent a scene in a 2D game. I don't want to re-create these scenes if they've been created before and I want the state information of each scene to remain while not in use.
Ben S
You should edit your question and add this to the text. I think it will influence the answers people give.
Alex B
In that case, you want the class to be both sealed and singleton. You could also consider to put the scene in a separate class, which is singleton and sealed, and then drop the singleton requirement on the original class.
erikkallen
A: 

I think you will be better off with a factory pattern here to be honest. Or use an IoC tool like Brian Dilley recommends. In the c# world there are loads, here are the most popular : Castle/windsor, StructureMap, Unity, Ninject.

That aside, I thought it would be fun to have a go at actually solving your problem! Have a look at this:

//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 is a class that properly implements the system. Any client code will get hold of the singleton WickedRoom class. NaughtyRoom does not implement the system properly, but even this class can't be instantiated twice. A 2nd instantiation results in an exception.

Noel Kennedy
That's very similar to my current implementation. My issue is that nothing is stopping NaughtyRoom or WickedRoom from not implementing their Get methods. I want to force the subclasses to implement those Get methods.
Ben S
+2  A: 

Please reconsider. You do NOT want to use singletons here. You are making some functionality available to users who derive from your class. That's fine. But you're also dictating one specific way in which it must always be used, and for absolutely no reason. That is not good.

It may make sense to only instantiate one object of this class the majority of the time, but in that case, simply just instantiate the object once. It's not like you're very likely to accidentally instantiate a dozen objects without noticing. Moreover, how can you tell that having two instances will NEVER be useful? I can think of several cases even now.

Unit testing: You might want each test to instantiate this object, and tear it down again afterwards. And since most people have more than one unit test, you'll need to instantiate it more than once.

Or you might at some point decide to have multiple identical/similar levels in your game, which means creating multiple instances.

A singleton gives you two things:

  • A guarantee that no more than one instance of the object will ever be instantiated, and
  • Global access to that instance

If you don't need both these things, there are better alternatives. You certainly don't need global access. (globals are bad, and usually a symptom of bad design, especially in mutable data such as your game state)

But you don't need a guarantee that no more than one instances will ever be instantiated either. Is it the end of the world if I instantiate the object twice? Will the application crash? If so, you need that guarantee. But in your case, nothing bad will happen. The person instantiating the object simply uses more memory than necessary. But he might have a reason.

Simply put in the class documentation that this is a very big and expensive class, and you shouldn't instantiate it more often than necessary. Problem solved. You don't remove flexibility that might turn out to be useful later on, you don't grant global access to data for no reason. Because you can control who can see the object, you don't need to drown it in locks that will become a bottleneck in multithreaded applications. You don't have hidden dependencies scattered throughout your code, making it harder to test and harder to reuse.

jalf