views:

115

answers:

3

We have the following class hierarchy:

public interface IManager {
   object GetObject(int);
}

public class BaseManager : IManager ...

public class XManager : BaseManager {
   ...
   public static XManager Instance;
}

public class YManager : BaseManager {
   ...
   public static YManager Instance;
}

public static class ManagerFacade {
   private static IManager GetManager(type);
   public static object GetObject(type, int) { 
     return GetManager(type).GetObject(int); }
}

How would you implement the GetManager() function?

Is it possible to collect these types and their instances (or instance creating delegates) in a static dictionary in the static constructors of these classes? (Thank you Jon, I only remembered "something is different in .Net 4", but not the details)

Other ways would use class attributes or descendents of BaseManager or looking for implementations of IManager.

What is the preferred solution?

+1  A: 

I think the most common approach is to use something like IServiceProvider.

leppie
my question is: how do you implement the IServiceProvider.GetService() method
devio
@devio: `System.ComponentModel.Design.ServiceContainer` is an implementation of `IServiceProvider`.
leppie
+2  A: 

The fact that static members are involved makes me think that a dictionary-based approach is the best, unless you want to invoke reflection and figure out the method for retrieving the singleton. This is a working sample code I was able to come up with:

// Added another interface - GetInstance
public interface IManager
{
    object GetObject(int i);
}

// made BaseManager abstract; you don't have to do that, but then
// remember to make everything virtual, etc etc.
public abstract class BaseManager : IManager
{
    public abstract object GetObject(int i);
}

The child classes each implement a static constructor to create the singleton instance in my example, which is way too simplistic, but I'm sure you have a better way to do this:

public class XManager : BaseManager
{
    public static XManager Instance;

    static XManager() { Instance = new XManager(); }

    public override object GetObject(int i)
    {
        return "XManager Instance: index was " + i.ToString();
    }
}

public class YManager : BaseManager
{
    public static YManager Instance;
    static XManager() { Instance = new YManager(); }

    public override object GetObject(int i)
    {
        return "YManager Instance: index was " + i.ToString();
    }
}

The ManagerFacade would implement the dictionary this way:

public static class ManagerFacade
{
    private static readonly Dictionary<Type, IManager> managerInstances 
      = new Dictionary<Type, IManager>()
    {
        {typeof(XManager), XManager.Instance},
        {typeof(YManager), YManager.Instance}
    };

    private static IManager GetManager<T>() where T: IManager
    {
        return managerInstances[typeof(T)];
    }

    public static object GetObject<T>(int i) where T: IManager
    {
        return GetManager<T>().GetObject(i);
    }
}

The console app to test out the manager facade:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(ManagerFacade.GetObject<XManager>(2).ToString());
        Console.WriteLine(ManagerFacade.GetObject<YManager>(4).ToString());

        // pause program execution to review results...
        Console.WriteLine("Press enter to exit");
        Console.ReadLine();
    }
}

Console output:

XManager Instance: index was 2
YManager Instance: index was 4
Press enter to exit

I'm sure that there's more elegant ways to do this, but I just wanted to illustrate how to set up and access the dictionary to support the singletons.

code4life
Thank you! What I was missing was the Facade's Dictionary and its initialization.
devio
@devio - glad you found the information useful!
code4life
A: 

The best approach for this depends on the usage you expect. If you have only a few managers, and you expect to increase the number only occasionally, then I would go with the straightforward approach:

class ManagerFacade {
  XManager getXManager() {...}
  ....
}

This reduces errors be making sure that the caller always gets the type of manager they are expecting. However it does mean you need to change ManagerFacade every time you add a type of manager, so it's not appropriate in those cases.

If you need to add new managers fairly frequently then implement

class ManagerFacade {
      IManager getManager(type) {...}
      ....
    }

hardcoding the logic behind getManager() and using an enum or equivalent as the type. This means you need to rewrite the method when you add a manager, but the interface doesn't change.

If your managers are changing frequently or dynamically then keep a dictionary behind the scenes which you populate with the appropriate managers. Use a String as 'type', so that you don't have to change an 'enum' class when you add a manager. You can lazily initialise the managers if you need to.

Incidentally, are you sure you need a singleton here? Just because you want to have ManagerFacade always return the same instance of XManager doesn't mean XManager needs to be a Singleton. You can make XManager internal to ManagerFacade, or restrict the manager creation to be done through a static method.

DJClayworth