Suppose I have a Widget class that is part of a framework used independently by many applications. I create Widget instances in many situations and their lifetimes vary. In addition to Widget’s instance specified methods, I would like to be able to perform the follow class wide operations:
- Find a single Widget instance based on a unique id
- Iterate over the list of all Widgets
- Remove a widget from the set of all widgets
In order support these operations, I have been considering two approaches:
-
Container class – Create some container or manager class, WidgetContainer, which holds a list of all Widget instances, support iteration and provides methods for Widget addition, removal and lookup. For example in C#:
public class WidgetContainer : IEnumerable<Widget> { public void AddWidget(Widget); public Widget GetWidget(WidgetId id); public void RemoveWidget(WidgetId id); }
-
Static class methods – Add static class methods to Widget. For example:
public class Widget { public Widget(WidgetId id); public static Widget GetWidget(WidgetId id); public static void RemoveWidget(WidgetId id); public static IEnumerable<Widget> AllWidgets(); }
Using a container class has the added problem of how to access the container class. Make it a singleton?..yuck! Create some World object that provides access to all such container classes?
I have seen many frameworks that use the container class approach, so what is the general consensus?
1
The modern consensus is that you should not do this at all.
Having a ‘all instances of this class’ design has proven to be troublesome in many aspects, the foremost being with regards to concurrency. Do you want all widgets, or all widgets owned by your thread? Do you really want to make that all widget list threadsafe? Remember that unit tests are almost always done in parallel, so “my app is single threaded” might not be good enough.
The other problem you run into is in your design. With the ‘one and only’ list of anything, be it global, static or singleton you’ll quickly run into issues when you need more than one. You’ll run into issues with temporary widgets or offscreen widgets if they’re automatically added to the list. You’ll run into errors where you forgot to add them to the list if they’re not automatically handled.
Once you don’t have the ‘all instances of a class’ requirement, then you end up with a plain old collection. Whatever consuming code is working with the widgets (even if that’s some framework that is in turn consumed) can orchestrate how the collection is used and how/when the widgets get put in there. The widgets themselves should not care.
3
This seems to be a good candidate for using an IoC (Inversion of Control) container. Instead of having to create the container yourself, you just rely on a generic container part of an IoC library which you can call upon.
For example you would define an interface:
public interface IWidgetHandler
{
void AddWidget(IWidget widget);
IWidget GetWidget(int widgetId);
void RemoveWidget(int widgetId);
IEnumerable<Widget> GetAllWidgets();
}
Then you define a class that implements that interface.
public class WidgetHandler : IWidgetHandler
{
public void AddWidget(IWidget widget)
{
throw new NotImplementedException();
}
public IWidget GetWidget(int widgetId)
{
throw new NotImplementedException();
}
public void RemoveWidget(int widgetId)
{
throw new NotImplementedException();
}
public IEnumerable<Widget> GetAllWidgets()
{
throw new NotImplementedException();
}
}
You would register how you should find instances of WidgetHandlers with an IoC container.
Generally, an IoC container has some sort of registration method so you can call
MyIoCContainer.Instance.Register<IWidgetHandler, WidgetHandler>();
Now whenever you need a widget handler you just tell IoC to give you an instance of one.
IWidgetHandler widgetHandler = MyIoCContainer.Instance.Resolve<IWidgetHandler>();
The IoC container can also control the lifetime of your objects.
Have a look at the ninject documentation if you want further details on getting up to speed with IoC. There are also a few more options out there (unity, autofac, Castle just to name a few). In fact there is a CommonServiceLocator library that provides a common interface for all, with the lowest common denominator of useful functionality (should you want to swap between them)
It allows your code to be more testable when compared to static methods classes since these can be mocked when a piece of code depends on an IWidgetHandler.
Instead of serving up a real widget handler, IoC (or the test framework) can serve up an instance of a FakeWidgetHandler which can be used to test the code that depends on this module by serving up various fake responses to the code and asserting that method parameters are passed correctly down to the IWidgetHandler dependency.
Good luck!
6
There is absolutely no difference between having a Singleton object and using static methods on the Widget class. Widget.GetWidget and WidgetContainer.Instance.GetWidget carry exactly the same problems.
Where and in what form are your widgets when the application isn’t running? That will help solve your problem.
If they’re in a database or on the filesystem then you don’t need a single-instance object to access them, for example. Any number of WidgetContainer objects can get them from the file-system or database, independently. You can implement a caching system if performance is your concern.
7