Monday, April 21, 2008 12:49 PM
At a presentation on mock objects this past weekend, I slipped into my normal rant regarding the inherit evils of Singletons. This inevitably lead into a brief discussion of the Monostate pattern as an alternative. Since then, I've received a few questions requesting more information on this pattern and I've been a little surprised to find that there doesn't seem to be much accessible information on it spread across the internet. Hopefully we can remedy that a bit with this post...
What's wrong with the Singleton
I'll spare everyone the full breadth of my rant regarding my issues with the Singleton pattern, as there are plenty others out there who can state them much more eloquently than I. However, in the interest of context, here's a brief run down of the issues...
- The Singleton leans heavily on public, static members in order to get its job done. Public static members undo many of the benefits of true object oriented design. Specifically, they can't be overridden which pretty much takes inheritance and polymorphism out of the picture.
- The Singleton is simply a glorified a global variable which introduces unnecessary coupling between objects. Imagine your objects as each of series of loosely connected links in a chain, each of which only talks to its immediate neighbors yet still maintains a flexible, yet firm, linkage. Then bunch each of those links directly on top of one another and drive a stake through the middle of them, irreversibly pinning them together forever. That stake is the Singleton.
- Since they're static, they can't be mocked with most mocking frameworks. This can make proper isolated unit testing of components very difficult. This is aggravated by the fact that Singletons are most often reserved for objects which will be simultaneously in use by multiple client objects. This means that your unit tests will also be danger of being dependent on the state that the Singleton object was left in by another unit test if your tests are not properly isolated.
Notice that all of the issues with Singletons are related purely to their implementation. The issue of shared state isn't really a problem. In fact, there are many cases where the concept of shared state makes perfect sense, just not the static way we must go about it. What we need, is a way to accomplish the same goals as the Singleton, without the headaches that come along with it.
Enter the Monostate Pattern
The Monostate pattern is just the answer we need. The Monostate gives us the singularity of state that we so treasure in the Singleton, but without all of the static headaches that come along with it. Let's take a look at the following class...
public class MenuController : IMenuController
{
private int _activeMenuIndex;
public int ActiveMenuIndex
{
get { return _activeMenuIndex; }
set { _activeMenuIndex = value; }
}
public int GetValueOfCurrentMenu()
{
// Fake value, just for example... return 10;
}
}
This class represents a menu controller for an application, which is commonly implemented as a Singleton. This class has nothing to it with the exception of a property containing the currently active menu and a (fake) method which will return any value associated with the currently active menu. The reason we typically only want one instance of this class floating around our application is because we don't want multiple controllers fighting for control of a single menu system. As stated above, normally this class would be a Singleton, but let's try it with a Monostate instead...
public class MonostateMenuController : IMenuController
{
private static MenuController _menuController;
static MonostateMenuController()
{
_menuController = new MenuController();
}
public int ActiveMenuIndex
{
get { return _menuController.ActiveMenuIndex; }
set { _menuController.ActiveMenuIndex = value; }
}
public int GetValueOfCurrentMenu()
{
return _menuController.GetValueOfCurrentMenu();
}
}
This class encapsulates the actual MenuController class, but does it in such a manner that clients of the class are guaranteed to always work with the same underlying instance of MenuController. Let's discuss a few key parts of this class...
- Both this class and the MenuController class implement the IMenuController interface. This isn't completely necessary, but if you are comfortable coding to interfaces instead of concrete classes then this is a great way to shield clients of your class from the fact that they are actually working with a proxy to the MenuController instance, and not the actual MenuController instance itself.
- Notice that this class contains both a static instance to _menuController as well as a static constructor. The key to static members is that they are shared amongst all instances of the class (remember that the static keyword is actually shared in Visual Basic). In this case, that means the _menuController instance will be shared across all instances of MonostateMenuController and the static constructor will only ever be executed once.
- Finally, notice that all of the methods implementing the IMenuController interface simply delegate to the static _menuController field. This ensures that no matter how many instance of MonostateMenuController are created, when their methods are called the result will always come from the same instance of MenuController. It this fact which guarantees the singularity of state which we strive for with the Singleton.
A key point to remember here is that the Monostate pattern is actually implemented as a family of classes, not a single class as is the case with the Singleton pattern. This does lead to a slight increase of complexity on side of your implementation code. However, your client code is actually simplified as the Monostate class is instantiated just the same as any other class, as is seen below...
IMenuController menuController = new MonostateMenuController();
menuController1.ActiveMenuIndex = 5;
Summary
Using this technique, we can reap all of the benefits that the Singleton pattern gives us, without the headaches that generally come along with them. We also remove the static members which allow us to code to interfaces as well as easily create mocked instances which lead to greater malleability of our code as well as improved testability. You can download the sample code from here.