• Post last modified:19/04/2020
  • Reading time:4 mins read
  • Post comments:0 Komentarzy

Singleton design pattern jest jednym z mniej popularnych i mniej stosowanych wzorców projektów w świecie JavaScript. Nie mniej jednak znajdą się przypadki gdy singleton okaże się dobrym rozwiązaniem.

Wzorce projektowe JavaScript

Wpis ten jest częścią serii wpisów dotyczących wzorców projektowych stosowanych w JavaScript. Pozostałe wzorce oraz ich opis można znaleźć na głównej stronie całej serii.

Singleton

Singleton jest wzorcem, który pozwala na stworzenie tylko jednej instancji obiektu z klasy bądź konstruktora funkcyjnego. Więcej na temat wzorca konstruktora można znaleźć w tym wpisie. W przypadku wielokrotnego wywoływania tej samej klasy zawsze będziemy otrzymywali tą samą instancję, która została stworzona podczas pierwszego wywołania. To tyle jeżeli chodzi o samą definicję singletonu. Jak widać nie jest ona zbyt skomplikowana.

Singleton przez niektórych klasyfikowany jest jako anty-wzorzec, ponieważ łamie zasady programowania obiektowego i bywa nadużywany jako alternatywny sposób na tworzenie zmiennych globalnych.

Jako przypadki użycia singletonu możemy tutaj wymienić np.:

  • Obiekty konfiguracyjne. Nie chcemy może za każdym razem generować nowego obiektu z danymi konfiguracyjnymi. Zamiast tego możemy zwracać już raz wygenerowane dane.
  • Połączenie z bazą danych. Zazwyczaj chcemy ustanowić jedno połączenie z bazą a nie inicjować wielu połączeń przy każdym zapisie/odczycie.
  • Logger danych. Duplikowanie logów z aplikacji może wprowadzić sporo problemów przy debugowaniu potencjalnych problemów.

Implementacja

Przedstawimy sobie dwa przykładowe sposoby na to w jaki sposób można stworzyć singleton w JavaScript. We wpisie dotyczącym module design pattern omawialiśmy sobie tworzenie modułów przy użyciu funkcji IIFE, więc również tutaj jedna implementacja będzie oparta na IIFE:

/* --- Singleton --- */
const AppConfig = (function() {
  let config; // prywatna wartość, która zostanie zainicjowana tylko raz

  function initializeConfiguration(initData) { // funkcja konstruktora
    this.randomNumber = Math.random();
    initData = initData || {};
    this.number = initData.number || 5;
  }

  // publiczna część singletonu - API modułu
  return {
    getConfig: function(values) {
      // inicjujemy singleton tylko jeden raz
      if (config === undefined) {
        config = new initializeConfiguration(values);
      }
      // przy kolejnych wywołaniach zawsze zwracamy już te same dane
      return config;
    }
  };
})();

const configObject = AppConfig.getConfig({ number: 8 });
console.log(configObject); // patrz zdjęcie poniżej

const configObject2 = AppConfig.getConfig({ number: 1 });
console.log(configObject2); // patrz zdjęcie poniżej

console.log(configObject.config); // undefined
console.log(config); // ERROR

Oto co zobaczymy w konsoli:

wynik dla przykładu w konsoli

Jak widać na powyższym screenie zarówno losowo generowana wartość dla randomNumber jak i determinowany przez konstruktor number w obydwu przypadkach mają dokładnie takie same wartości. Sama wartość zmiennej config nie może również zostać zmieniona poza modułem stworzonym przez IIFE.

Użycie klasy

W momencie gdy w JavaScript pojawiły się zdefiniowane w standardzie ES6 klasy możemy stworzyć singleton nieco łatwiej i czytelniej. Poniżej znajduje się jeden z najprostszych sposobów.

W tym artykule zajmujemy się samym wzorcem projektowym i nie będziemy wchodzić w szczegóły na temat klas. Więcej do poczytania np. tutaj

class AppConfig { 
  constructor(number = 5){ 
      if(AppConfig.exists){ 
        return AppConfig.instance 
      } 
      this.randomNumber = Math.random();
      this.number = number;
      AppConfig.exists = true; 
      AppConfig.instance = this; 
      return this 
  }  
} 

const configObject = new AppConfig(8);
const configObject2 = new AppConfig(1);

console.log(configObject);
console.log(configObject2);
console.log(configObject === configObject2); // true
Wynik w konsoli

Jak widać w tym przypadku również obiekty, które zostały zwrócone z klas są identyczne.

Singleton design pattern można zaimplementować na kilka różnych sposobów. Powyższe dwa przykłady pokazują jedne z najłatwiejszych sposobów na osiągniecie tego celu. Mimo, iż singleton w aplikacjach JavaScript nie jest wykorzystywany zbyt często – zdecydowanie warto wiedzieć o jego istnieniu.

Kamil Józwik

Frontend developer👨‍💻 Autor kursów na frontschool.pl, małego narzędzia dla programistów - frontbook.dev oraz kolejnych postów na tym blogu. Ulubiony stos technologiczny to React wraz z TypeScript oraz wszystko, co "nowe" w tym pięknym dynamicznym front end-owym świecie 🙂
Subscribe
Powiadom o
guest
0 Comments
Inline Feedbacks
View all comments