|
Home |
Injected SingletonStructureMap now provides an optional mechanism to create and return a single instance per instance configuration as an alternative to the Singleton pattern I am calling an "Injected Singleton." A PluginFamily can now be configured to create only one instance for each named instance. Singleton Design PatternEnsure a class only has one instance, and provide a global point of access to it. The Singleton pattern from the "Gang of Four" book is undeniably an essential part of any developer's toolbox. The motivation for using a Singleton is to insure that only one instance of a class is created within the application. Mechanically, this is accomplished by making a public class with only private constructors. The class will create and store one instance in a static field. Other classes can attain a reference to the single instance by calling a static method on the singleton class. Below is an example Singleton.
public class SingletonExample
{
private static SingletonExample _instance = new SingletonExample();
public static SingletonExample Instance
{
get
{
return _instance;
}
}
private int _count;
private SingletonExample()
{
}
public void Increment()
{
_count++;
}
public int Count
{
get { return _count; }
}
}
public class Transactor
{
public Transactor(){}
public void DoSomething()
{
SingletonExample.Instance.Increment();
/* Do stuff */
}
}
UsagesWhile the Singleton pattern is probably overused, here are some scenarios where a Singleton or equivalent is valuable.
Unit Testing with a SingletonThe primary downfall of a Singleton is a negative impact on automated unit testing. Test Driven Development with automated unit tests is most effective when unit tests are small, isolated, and running with known boundary conditions. Since a Singleton class is responsible for creating its single instance, a Singleton may often be in some sort of "dirty" state during later unit test runs. The unit test is no longer contained within known boundary conditions. The unit test might give false results. This problem can be overcome by providing some sort of "Reset()" method on the Singleton to clear the state prior to running a unit test. The worst, most tightly coupled unit tests I have ever written, mocked the underlying dependencies of a Singleton class to measure the interaction of an MVC controller class with a Singleton repository. The test class for the controller was tightly coupled to the internal workings of the repository class. In this case it would be vastly more effective and understandable to mock the repository instead. This was not possible because the repository class was only accessable as a Singleton instance.Since the only way to get an object instance of the repository was to call the class itself, there was no easy way to substitute a Mock object in a unit test to establish a known boundary condition. This problem can be eliminated by making all dependencies be on an abstracted interface and using Dependency Injection to attach the shared instance instead of referencing the Singleton directly. For more information, try here: Singletons are Evil. Singleton with StructureMapStructureMap can be configured to create a shared instance to accomplish the same goals as a Singleton, while still allowing for polymorphic substitutions of the dependency. The following example shows the usage of Injected Singleton in StructureMap with a modification of the previous code sample.
[PluginFamily("Concrete", IsSingleton = true)]
public interface ISingleton
{
void Increment();
int Count {get;}
}
[Pluggable("Concrete")]
public class ConcreteSingleton : ISingleton
{
private int _count = 0;
public void Increment()
{
_count++;
}
public int Count
{
get { return _count; }
}
}
public class Transactor
{
public Transactor(){}
public void DoSomething()
{
ISingleton singleton = (ISingleton) ObjectFactory.GetInstance(typeof(ISingleton));
/* Do stuff */
}
}
A few points about the code sample:
An NUnit tester class like the following can now use the Mock Injection feature of StructureMap to substitute a mocked ISingleton inside of a unit test.
[TestFixture]
public class TransactorTester
{
IMock _singletonMock;
[SetUp]
public void SetUp()
{
_singletonMock = ObjectFactory.Mock(typeof(ISingleton));
}
[TearDown]
public void TearDown()
{
ObjectFactory.ResetDefaults();
}
[Test]
public void Test1()
{
_singletonMock.ExpectCall("DoSomething");
Transactor transactor = new Transactor();
transactor.DoSomething();
_singletonMock.Verify();
}
}
A PluginFamily can also be explicitly configured to be an Injected Singleton in the StructureMap.config file like the following xml. The <Interceptors> node instructs StructureMap to make the PluginFamily an injected singleton.
|