Durch einen Singleton-Provider bzw. einer -Base, die jeweils mit generischen Typen in C# 2.0 (Generics) implementiert werden, wird der Freiheitsgrad eines Softwarentwicklers stark eingeschränkt. Statt dieses Entwurfsmuster immer wieder pro Klasse neu zu implementieren, ist nur die einmalige Implementierung eines Singleton-Providers notwendig. Dieser ermöglicht dann die Anwendung auf (fast) jede Klasse. Das heißt konkret, dass mitttels SingletonProvider<MeineKlasse>.Instance.MethodeA() auf die MethodeA() im Sinne eines Singleton zugegriffen werden, ohne dass ein Singleton-Entwurfsmuster in der Klasse MeineKlasse überhaupt implementiert wurde.
SingletonProvider<MeineKlasse>.Instance.MethodeA()
MethodeA()
MeineKlasse
Doch damit nicht genug. Der Freiheitsgrad kann weiter eingeschränkt werden. Und zwar indem der Singleton-Provider bzw. die -Base bei Bedarf einfach automatisch generiert oder sogar als Bibliothek eingebunden werden. Solche Techniken forciert insbesondere der Ansatz der Software Factories, wie er von Jack Greenfield und Keith Short definiert wurde. Grund genug für Microsoft, für die Entwicklung von und mit Software Factories auch entsprechend benötigte Tools wie das Guidance Automation- und das DSL-Toolkit (GAT) zur Verfügung zu stellen und in naher Zukunft durch weitere zu ergänzen.
Inwieweit mit den wichtigsten Konzepten wie Automatisierung (Guidance and Automation) oder auch der domänenspezifischen Modellierung (DSM) die Ziele von Software Factories erreicht werden kann, zeigt der Artikel "Industrialisierung der Wiederverwendung – Software Factories"¹, ².
Nachfolgend ist ein Singleton-Provider und eine -Base dargestellt, die jeweils getrennt herunterladbar sind. Ein Visual Studio 2005-Projekt gibt es hier: Singleton.zip (40,21 KB).
Stellt ein Singleton-Muster zur Verfügung. Dabei auftretene Ausnahmen bei der Initialisierung werden gefangen und können abgefragt bzw. erneut geworfen werden.
using System; namespace Patterns { /// <summary> /// Stellt ein Singleton-Muster direkt zur Verfügung. /// </summary> /// <typeparam name="T">Der Typ, für den ein Singleton erstellt werden soll.</typeparam> public sealed class SingletonProvider<T> where T : new() { #region Classes /// <summary> /// Innere Klasse für Singleton-Pattern. /// </summary> private class SingletonCreator { /// <summary> /// Aufgetretene Exception im Initialisieren/Instanzieren. /// </summary> internal static readonly Exception s_InitializationException = null; /// <summary> /// Die Instanz. /// </summary> internal static readonly T s_Instance = default(T); /// <summary> /// Inititialisiert eine Instanz des Typs T. /// </summary> static SingletonCreator() { try { s_Instance = new T(); } catch (Exception exception) { s_Instance = default(T); s_InitializationException = exception.InnerException; } } } #endregion #region Properites /// <summary> /// Ruft die Exception ab, aufgrund deren die Instanziierung/Initialisierung fehlgeschlagen ist. /// </summary> public static Exception InitializationException { get { return SingletonCreator.s_InitializationException; } } /// <summary> /// Ruft die Instanz ab. /// </summary> public static T Instance { get { return SingletonCreator.s_Instance; } } #endregion #region SingletonProvider /// <summary> /// Initialisiert eine neue Instanz der SingletonProvider-Klasse. /// </summary> private SingletonProvider() { } #endregion #region Methods /// <summary> /// Prüft ob beim Initialisieren einer Instanz einer Klasse eine Exception aufgetreten ist. /// Wenn ja, wird diese Exception geworfen. /// </summary> public static void CheckInitialization() { if (SingletonProvider<T>.InitializationException != null) { throw SingletonProvider<T>.InitializationException; } } #endregion } }
SingletonProvider.zip (1 KB)
Stellt ein Singleton-Muster über Vererbung zur Verfügung. Auftretene Ausnahmen bei der Initialisierung werden gefangen und können abgefragt bzw. erneut geworfen werden.
using System; using System.Reflection; namespace Patterns { /// <summary> /// Stellt ein Singleton-Muster über Vererbung zur Verfügung. /// </summary> /// <typeparam name="T">Der Typ, für den ein Singleton erstellt werden soll.</typeparam> public class SingletonBase<T> where T : class { #region Classes /// <summary> /// Innere Klasse für Singleton-Pattern. /// </summary> private class SingletonCreator { /// <summary> /// Aufgetretene Exception im Initialisieren/Instanzieren. /// </summary> internal static readonly Exception s_InitializationException = null; /// <summary> /// Die Instanz. /// </summary> internal static readonly T s_Instance = default(T); /// <summary> /// Inititialisiert eine Instanz des Typs T. /// </summary> static SingletonCreator() { try { Type typeT = typeof(T); ConstructorInfo constructorInfo = null; constructorInfo = typeT.GetConstructor( BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (constructorInfo == null) { // falls kein privater Konstruktur vorhanden constructorInfo = typeT.GetConstructor( BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public, null, Type.EmptyTypes, null); } Object o = constructorInfo.Invoke(null); s_Instance = o as T; } catch (Exception exception) { s_Instance = default(T); s_InitializationException = exception.InnerException; } } } #endregion #region Properites /// <summary> /// Ruft die Exception ab, aufgrund deren die Instanziierung/Initialisierung fehlgeschlagen ist. /// </summary> public static Exception InitializationException { get { return SingletonCreator.s_InitializationException; } } /// <summary> /// Ruft die Instanz ab. /// </summary> public static T Instance { get { return SingletonCreator.s_Instance; } } #endregion #region Methods /// <summary> /// Prüft ob beim Initialisieren einer Instanz einer Klasse eine Exception aufgetreten ist. /// Wenn ja, wird diese Exception geworfen. /// </summary> public static void CheckInitialization() { if (SingletonBase<T>.InitializationException != null) { throw SingletonBase<T>.InitializationException; } } #endregion } }
SingletonBase.zip (1 KB)
SingletonProvider.<TestClass>.Instance.WriteHelloWorldOnConsole(); ... class TestClass { public void WriteHelloWorldOnConsole() { Console.WriteLine("Hello World"); } }
Und abschließend noch ein Dankeschön an Dirk, der mich mit seinem Blog-Eintrag Das Singleton - das Unbekannte Wesen inspiriert hat.
¹ OBJEKTspektrum 03/2006, Gunther Lenz, Torsten Weber, "Industrialisierung der Wiederverwendung – Software Factories"² Mehr Informationen zu den Themen Singleton, domänenspezifischen Sprachen und Software Factories:
Remember Me
a@href@title, b, blockquote@cite, em, i, strike, strong, sub, sup, u