poniedziałek, 10 stycznia 2022

[2] Wzorce projektowe - C# - Singleton

W tym poście chciałbym krótko opisać wzorzec projektowy Singleton.


Dokładniejszy opis można znaleźć pod tym linkiem.  

Ten wzorzec ma zastosowanie w przypadku gdy w projekcie potrzebna jest tylko jedna instancja klasy. Np. wywołująca połączenia Klient/Serwer lub nawiązująca połączenie z bazą danych. W standardowej implementacji klasy każdorazowe wywołanie instancji klasy spowoduje utworzenie nowego obiektu. Gdy zostanie zastosowany wzorzec Singleton to za każdym razem gdy wywołana zostanie nowy obiekt klasy, zwrócony zostanie poprzednio utworzony obiekt (ponieważ instancja klasy jest statyczna). Dodatkową zaletą jest inicjalizacja klasy dopiero gdy jest ona faktycznie potrzebna. 

Ponieważ krótki przykład, przygotowany w oparciu o informacje udostępnione w linku powyżej.

  1. namespace SingletonTest
  2. {
  3.     class Singleton
  4.     {
  5.         private static Singleton _singleton_instance;
  6.  
  7.         private static readonly object _lock = new object();
  8.  
  9.         public int TestValue1 { get; set; }
  10.         public int TestValue2 { get; set; }
  11.  
  12.         public Singleton() { }
  13.  
  14.         public static Singleton get_singleton(int testValue1, int testValue2)
  15.         {
  16.             if (_singleton_instance != null)
  17.             {
  18.                 return _singleton_instance;
  19.             }
  20.             //Blokada, jeśli nastąpi wywołanie kodu z kilku wątków.
  21.             //Tylko pierwszy wątek który się dostanie rozpocznie inicjalizację
  22.             lock (_lock)
  23.             {
  24.                 if (_singleton_instance == null)
  25.                 {
  26.                     _singleton_instance = new Singleton();
  27.                     _singleton_instance.TestValue1 = testValue1;
  28.                     _singleton_instance.TestValue2 = testValue2;
  29.                 }
  30.             }
  31.             return _singleton_instance;
  32.         }
  33.     }
  34.  
  35.     class Program
  36.     {
  37.         static void Main(string[] args)
  38.         {
  39.             Singleton singleton1 = Singleton.get_singleton(12, 13);
  40.             Singleton singleton2 = Singleton.get_singleton(50, 74);
  41.  
  42.             Console.WriteLine(singleton1.TestValue1 + "\r\n");
  43.             Console.WriteLine(singleton1.TestValue2 + "\r\n");
  44.  
  45.             Console.WriteLine(singleton2.TestValue1 + "\r\n");
  46.             Console.WriteLine(singleton2.TestValue2 + "\r\n");
  47.  
  48.             Console.ReadKey();
  49.         }
  50.     }
  51. }

W powyższym przykładzie wykorzystywany jest lock, który zablokuje wywołanie klasy jeśli jest ona wywołana z wielu wątków, które zadziałają jednocześnie. Pierwszy z nich który się dostanie do funkcji utworzy nową instancję klasy. Pozostałe zwrócą już utworzoną instancję klasy.

Modyfikacja obiektu klasy singleton1 czy singleton2 spowoduje zmianę w obiekcie statycznym, czyli każdorazowo zmieniany będzie ten sam obiekt czyli _singleton_instance. Projekt zachowa się identycznie w przypadku wywołania kilku różnych wątków. Z tą różnicą, że obiekt początkowy zostanie utworzony przez wątek, który dostanie się tam pierwszy.