niedziela, 28 marca 2010

Singleton Pattern

Kolejnym wzorcem projektowym, który omówię jest Singleton. Jest to jeden z najprostszych wzorców projektowych, ale i jedyny który ma tak wiele różnych implementacji.

Wzorzec ten pozwala na tworzenie tylko jednego obiektu. Spytasz po co mi tylko jeden obiekt danej klasy? Przydaje się to w wielu miejscach aplikacji. Przykładowo kilka zastosowań to: ustawienia aplikacji, dostęp do pliku (zapis), tworzenie logów.
Zapewne może się wydawać, że tworzenie specjalnego wzorca dla tego typu zastosować jest nieopłacalne. Ktoś może powiedzieć: skoro mamy utworzyć jeden obiekt to zróbmy jedną zmienną globalną i bez cyrków mamy to co nam potrzeba. Jest jednak jeden minus tego rozwiązania. Jeśli tworzymy zmienną globalną to utworzyć musimy ją przy starcie naszej aplikacji a jeśli nasza aplikacja działa bardzo długo to tym gorzej dla nas - marnujemy cenne zasoby. Singleton pozwala na utworzenie siebie tylko wtedy kiedy jest na prawdę potrzebny.

Spójrzmy na diagram UML:
Jak widać mamy tu do czynienia z jedną klasą Singleton. Konstruktor klasy jest prywatny, dzięki czemu zapobiegamy tworzeniu obiektów tej klasy. Pole instance przechowuje utworzony obiekt Singletonu a metoda GetInstance tworzy lub zwraca już istniejący obiekt Singletona.

Zobaczmy na przykładową implementację tego wzorca:
    1     class Singleton
    2     {
    3         private static Singleton _instance;
    4         public int i;
    5         private Singleton()
    6         {
    7         }
    8 
    9         public static Singleton GetInstance()
   10         {
   11             return _instance == null ? (_instance = new Singleton()) : _instance;
   12         }
   13     }
   14 
   15     class Program
   16     {
   17         static void Main(string[] args)
   18         {
   19             Singleton sing = Singleton.GetInstance();
   20             sing.i = 20;
   21             Console.WriteLine(sing.i);
   22             Singleton s = Singleton.GetInstance();
   23             Console.WriteLine(sing.i);
   24         }
   25     }

Implementacja wydaje mi się bardzo prosta w zrozumieniu. W klasie mamy statyczne pole _instance i metodę GetInstance(), która w zależności od tego czy jeszcze nie utworzono obiektu czy też utworzono zwraca odpowiednią wartość.

Kod przedstawiony powyżej, pomimo swojej prostoty, może sprawić nam nie lada problem w aplikacjach wielowątkowych. Podczas gdy nasz kod wykorzystywałby wiele wątków, mogło by dojść do sytuacji kiedy każdy wątek w tej samej chwili spróbowałby utworzyć instancję Singletonu - a wtedy katastrofa murowana. Najprostszym rozwiązaniem jest zastosowanie tworzenia obiektu w deklaracji:

    1 class Singleton
    2     {
    3         private static readonly Singleton _instance = new Singleton();
    4         public int i;
    5         private Singleton()
    6         {
    7         }
    8 
    9         public static Singleton GetInstance()
   10         {
   11             return _instance;
   12         }
   13     }

Framework .NET gwarantuje w takim przypadku, że pole jest thread safe. W dokumentacji na MSDN można znaleźć wiele takich adnotacji do istniejących klas w .NET.
Inną opcją jest tzw. podwójne sprawdzanie. Jednak podana wyżej metoda jest prostsza i szybsza w implementacji.

Wiele innych metod implementacji Singletonu można znaleźć na wielu internetowych stronach. Polecam zaznajomić się z innymi możliwymi konstrukcjami.

Brak komentarzy:

Prześlij komentarz