Sometimes you need global access to an object, because this
object may contain static properties, events, or methods you may access
throughout your application. Through this static object, the application
can be more easily decoupled from other related classes because more classes
can interact through the singleton and not as much by referencing the other
objects directly.
This is the singleton pattern; it has the benefits that it
maintains its own instance and provides this central access throughout the
application. The key benefit is the ability to expose events, which the other
classes in a project can subscribe to by attaching their own event handlers to
it. When the event fires, all registered objects are subscribed to that event;
this is similar to the Observer pattern.
How can you create a global instance? Let us use a
class called ServicesCentral, which provides access to key objects/collections
throughout an application. The class definition and the singleton reference
would appear as such:
Listing 11
public class ServicesCentral
{
private static readonly ServicesCentral _instance = new ServicesCentral();
public static ServicesCentral Instance
{
get { return _instance; }
}
private ServicesCentral()
{
//Instantiate any collection objects here
}
}
A static instance variable represents the ServicesCentral
class, which is exposed through the Instance property. This instance is
used internally to give access to the properties and methods, as we will see
next. The constructor of the singleton is also marked private or protected
so only the singleton can instantiate itself. Within that constructor, any
of the objects are instantiated so they are available to the user of the
singleton. Let us look at a few objects that can be exposed through the
singleton.
Listing 12
private MenuCollection _menus = null;
private ToolbarCollection _toolbars = null;
private MarkedListRepository _currentList = null;
public static MenuCollection Menus
{
get { return Instance._menus; }
}
public static ToolbarCollection Toolbars
{
get { return Instance._toolbars; }
}
public static MarkedListRepository CurrentList
{
get { return Instance._currentList; }
}
private ServicesCentral()
{
_menus = new MenuCollection();
_toolbars = new ToolbarCollection();
_currentList = new MarkedListRepository();
}
The singleton exposes a collection of menus, toolbars, and
other objects that are important to an application. The scenario above is
similar to exposing an API for an application, such as Word or Excel. All
of the necessary objects are instantiated in the private constructor. Because
the instance is instantiated internally, all objects are referenced through the
Instance property because this Instance property is the true object that holds
the singleton reference.
Notice how the private variables that hold references to
these objects are not static; instead, because they are exposed through the
Instance property and because Instance contains a reference to the singleton
object, it can access the appropriate internal variables. In addition to
existing properties, you can add helper properties/methods that are generally
helpful in your application. For instance, you can add the following.
Listing 13
public static CultureInfo CurrentCulture
{
get { return Thread.CurrentThread.CurrentCulture; }
}
public static CultureInfo CurrentUICulture
{
get { return Thread.CurrentThread.CurrentUICulture; }
}
public static string CurrentDirectory
{
get { return AppDomain.CurrentDomain.BaseDirectory; }
}
public static string GetFilePath(string fileName)
{
return CurrentDirectory + @"\" + fileName;
}
The list could go on, containing whatever centralized
objects, properties, or methods that are needed.