| << Previous | Suneido > Contents > Cookbook | Next >> |
Category: Coding
Ingredients
Problem
You want a single instance of a class that is available globally.
Recipe
Unlike other object oriented languages like Java or C++, Suneido does not allow static data members of classes. A class is a constant, it can't contain data members (other than other constants). The only place Suneido provides for global variables is the "Suneido" object. So to implement the Singleton pattern we store the instance in a member of the Suneido object. Here is the code:
Singleton
class
{
CallClass()
{
if not Suneido.Member?(.name())
Suneido[.name()] = new this
return Suneido[.name()]
}
Reset()
{
Suneido.Delete(.name())
}
name()
{
c = .Base() is Singleton ? this : .Base()
return Display(c).BeforeFirst(' ')
}
}
Rather than use a static method (such as "Instance()" to access the instance we can use the CallClass method. This method is used if the class is "called". This allows us to access the singleton as simply:
MySingleton().MyMethod()
The CallClass method creates the instance if it doesn't exist yet.
The Reset method allows you to destroy the instance (so it will be recreated).
The name of the class is used as the member name in the Suneido object. If name() is called on the instance, it uses the Base() method to get the class.
Singleton is an abstract base class, to use it you must define your own class that inherits from Singleton. For example:
Logger
Singleton
{
New()
{ .log = Object() }
Append(msg)
{ .log.Add(msg) }
Log()
{ return .log }
}
We could then use it like:
Logger().Append("starting")
...
Logger().Append("ending")
...
Print(Logger().Log())
Logger().Reset()
Unit Test
Here is a unit test for Singleton:
Singleton_Test
Test
{
Test_main()
{
c = Singleton_TestClass
AssertEq(c(), Suneido.Singleton_TestClass)
AssertEq(c().N, 0)
c().Inc()
AssertEq(c().N, 1)
c.Reset()
Assert(not Suneido.Member?('Singleton_TestClass'))
AssertEq(c().N, 0)
}
Teardown()
{
Suneido.Delete('Singleton_TestClass')
}
}
Because Singleton depends on the class name, we have to use a separately defined test singleton:
Singleton_TestClass
Singleton
{
New()
{ .N = 0 }
Inc()
{ ++.N }
}
Uses
Singleton is now used for the plugin manager (Plugins) and for the new AccessControl lock manager.
See Also
Design Patterns by Gamma, Helm, Johnson, and Vlissides includes the Singleton pattern.
Head First Design Patterns by Freeman & Freeman covers the pattern in a more "friendly" style.
Appendix > Idioms > CallClass for Singleton
| << Previous | Suneido > Contents > Cookbook | Next >> |