Singleton is a design pattern that enforces that only one instance of a given type may exist, while providing a single mechanism for accessing it globally.
Sections
Design Architecture
namespace danderson.io.DesignPatterns.Singleton { class T { private T() { } public static T Instance { get; private set; } } }
Design Usage
The design pattern is used when you need to guarantee that a type should exist only once for the lifetime of a program's execution. With this restriction, it also implies that there should be only one method of accessing the instance, which usually means it will be accessed in a static context. These restrictions provide several key benefits to the rest of the application. First, the rest of the program code no longer has to concern itself with creating instances of the class. Secondly, it allows the object's state to be persisted throughout the lifecycle of the application.
public void SaveDocument() { string settingsPath = @"C:\ProgramData\Company\App\Settings.xml"; using (var settings = new ApplicationSettings(settingsPath)) { if (!settings.Loaded) settings.Load(); UpdateFile(settings.OuputDirectory); } }
public void SaveChanges() { UpdateFile(ApplicationSettings.Current.OutputDirectory); }
The sample above demonstrates a program that is designed to load application settings on startup. Settings might be updated and saved throughout the execution of the program, but the code no longer has to concern itself with instantiating the class each time it is needed. In this design, the application's code does not have to concern itself with where the settings are loaded from, or how they were loaded. This results in good separation of concerns with a reduction in line count and complexity throughout the program. If a modification needs to be made to add additional settings, or how they are loaded, or how they are instantiated, you only have to modify the singleton class and the rest of the program can remain unchanged.
You should heavily consider whether a singleton design is appropriate. When implemented effectively it can have great benefits to your code base, but when implemented incorrectly it can have a negative impact on maintainability. Tight or high coupling can be a concern with this pattern, as well as testability. You might decide between a singleton with static access, or a non-singleton class that is created once in the program code and injected into other components as a dependency via constructor or property injection.
Design Implementation
Based on the samples in the design usage, here is a basic implementation of the singleton pattern.
namespace danderson.io.DesignPatterns.Singleton { using System.IO; using System.Reflection; using System.Xml.Linq; public sealed class ApplicationSettings { private static ApplicationSettings instance; private readonly string documentPath; private XDocument document; private ApplicationSettings() { var assembly = Assembly.GetExecutingAssembly(); var directory = Path.GetDirectoryName(assembly.Location); var filePath = Path.Combine(directory, @"Configuration.xml"); this.documentPath = filePath; } public static ApplicationSettings Current { get { return instance ?? (instance = new ApplicationSettings()); } } public string OutputDirectory { get { return document.Root.Element(nameof(OutputDirectory)).Value; } set { document.Root.Element(nameof(OutputDirectory)).Value = value; } } public void Load() { document = XDocument.Load(documentPath); } public void Save() { document?.Save(documentPath); } } }
Design Guidelines
DO: Use a singleton when exactly one instance of a type is needed, and you want to access it statically.
DO: Instantiate your singleton in only one location, either in its private constructor or via a private or internal factory.
DO: Ensure singleton instantiation is hidden from the rest of your code base.
DO: Define only a get
accessor if using a property to access your singleton instance.
CONSIDER: Using a static property named Instance
, Current
, or something that easily identifies the class as a singleton.
CONSIDER: Using a static method for accessing the singleton instance instead of a property when appropriate.
DO NOT: Use singleton if you need granular control over instantiation.
DO NOT: Use singleton if instantiation would require dependency injection that would break the pattern.