3.1 Singleton

Singleton to ogólny wzorzec projektowy, który gwarantuje, że aplikacja jednowątkowa będzie miała jedną instancję pewnej klasy i zapewnia globalny punkt dostępu do tej instancji.

Singel

Bardzo często początkujący programiści lubią łączyć metody narzędziowe w jakąś klasę statyczną - klasę, która zawiera tylko metody statyczne. Takie podejście ma szereg wad - np. nie można przekazać referencji do obiektu takiej klasy, takie metody są trudne do przetestowania i tym podobne.

Jako alternatywę zaproponowano rozwiązanie klasy singleton: klasę, która może mieć tylko jeden obiekt. Podczas próby utworzenia tego obiektu jest on tworzony tylko wtedy, gdy jeszcze nie istnieje, w przeciwnym razie zwracane jest odwołanie do już istniejącej instancji.

Istotne jest, aby możliwe było użycie instancji klasy, ponieważ w wielu przypadkach dostępna staje się szersza funkcjonalność. Na przykład ta klasa może implementować niektóre interfejsy, a jej obiekt może być przekazywany do innych metod jako implementacja interfejsu. Czego nie można zrobić za pomocą zestawu metod statycznych.

Plusy:

  • Metody są powiązane z obiektem, a nie klasą statyczną — obiekt można przekazać przez referencję.
  • Metody obiektowe są znacznie łatwiejsze do testowania i wyśmiewania.
  • Obiekt jest tworzony tylko wtedy, gdy jest to potrzebne: leniwa inicjalizacja obiektu.
  • Przyspieszenie pierwszego uruchomienia programu, jeśli jest wiele singli, które nie są potrzebne do uruchomienia.
  • Samodzielnie można dalej przekształcić w szablon-strategię lub kilka takich obiektów.

Wady:

  • Trudniej jest kontrolować wyścigi i opóźnienia między wątkami.
  • Trudno napisać wielowątkowego „samotnika” „z głowy”: dostęp do wieloletniego singletona, idealnie, nie powinien otwierać muteksu. Lepsze sprawdzone rozwiązania.
  • Konflikt między dwoma wątkami związany z niedokończonym pojedynczym wątkiem spowoduje opóźnienie.
  • Jeśli obiekt jest tworzony przez długi czas, opóźnienie może przeszkadzać użytkownikowi lub zakłócać czas rzeczywisty. W takim przypadku lepiej przenieść jego tworzenie na etap inicjalizacji programu.
  • Do testów jednostkowych wymagane są specjalne funkcje - na przykład, aby ustawić bibliotekę w trybie „niesamotnym” i całkowicie odizolować testy od siebie.
  • Wymagana jest specjalna taktyka testowania gotowego programu, ponieważ znika nawet pojęcie „najprostszej uruchamialności”, ponieważ uruchamialność zależy od konfiguracji.

3.2 Fabryka [Metoda]

Metoda fabryczna to ogólny wzorzec projektowy, który zapewnia podklasom (dziedziczącym klasy) interfejs do tworzenia instancji określonej klasy. W czasie tworzenia potomkowie mogą określić, którą klasę utworzyć.

Innymi słowy, ten szablon przekazuje tworzenie obiektów potomkom klasy nadrzędnej. Pozwala to na użycie nie konkretnych klas w kodzie programu, ale manipulowanie obiektami abstrakcyjnymi na wyższym poziomie.

Metoda fabryczna

Wzorzec ten definiuje interfejs do tworzenia obiektu, ale pozostawia podklasom decyzję, na której klasie oprzeć obiekt. Metoda fabryczna pozwala klasie delegować tworzenie podklas. Używane, gdy:

  • klasa nie wie z góry, które obiekty, z których podklas musi utworzyć.
  • klasa jest zaprojektowana w taki sposób, że tworzone przez nią obiekty są określone przez podklasy.
  • klasa przekazuje swoje obowiązki jednej z kilku podklas pomocniczych i planuje się ustalenie, która klasa przejmie te obowiązki.

3.3 Fabryka abstrakcyjna

Fabryka abstrakcyjna to ogólny wzorzec projektowy, który zapewnia interfejs do tworzenia rodzin powiązanych lub współzależnych obiektów bez określania ich konkretnych klas.

Wzorzec jest realizowany poprzez utworzenie abstrakcyjnej klasy Factory, która jest interfejsem do tworzenia komponentów systemu (np. dla interfejsu okienkowego może tworzyć okna i przyciski). Następnie pisane są klasy, które implementują ten interfejs.

abstrakcyjna fabryka

Jest używany w przypadkach, gdy program musi być niezależny od procesu i typów tworzonych nowych obiektów. Gdy konieczne jest utworzenie rodzin lub grup powiązanych ze sobą obiektów, z wyłączeniem możliwości jednoczesnego wykorzystania obiektów z różnych ich zestawów w tym samym kontekście.

Silne strony:

  • izoluje określone klasy;
  • upraszcza wymianę rodzin produktów;
  • gwarantuje kompatybilność produktów.

Załóżmy, że twój program działa z systemem plików. Następnie do pracy w Linuksie potrzebujesz obiektów LinuxFile, LinuxDirectory, LinuxFileSystem. A do pracy w Windwos potrzebne są klasy WindowsFile, WindowsDirectory, WindowsFileSystem.

Klasa Path, która jest tworzona za pomocą Path.of(), jest właśnie takim przypadkiem. Ścieżka nie jest tak naprawdę klasą, ale interfejsem i ma implementacje WindowsPath i LinuxPath. A jaki rodzaj obiektu zostanie utworzony, jest ukryty przed twoim kodem i zostanie określony w czasie wykonywania.

3.4 Prototyp

Prototyp to generatywny wzorzec projektowy.

Ten wzorzec definiuje rodzaje obiektów, które są tworzone przy użyciu instancji prototypu i tworzy nowe obiekty, kopiując ten prototyp. Pozwala oderwać się od implementacji i kierować się zasadą „programowania przez interfejsy”.

Interfejs/klasa abstrakcyjna na szczycie hierarchii jest określona jako typ powracający, a klasy potomne mogą zastąpić spadkobiercę, który implementuje tam ten typ. Mówiąc najprościej, jest to wzorzec tworzenia obiektu poprzez klonowanie innego obiektu zamiast tworzenia go za pomocą konstruktora.

Prototyp

Wzór służy do:

  • unikanie dodatkowego wysiłku związanego z tworzeniem obiektu w standardowy sposób (co oznacza użycie konstruktora, ponieważ w tym przypadku zostaną również wywołane konstruktory całej hierarchii przodków obiektu), gdy jest to zbyt kosztowne dla aplikacji.
  • unikaj dziedziczenia twórcy obiektu w aplikacji klienckiej, jak ma to miejsce w przypadku abstrakcyjnego wzorca fabrycznego.

Użyj tego wzorca projektowego, gdy Twój program nie dba o to, jak tworzy, komponuje i prezentuje produkty:

  • klasy z instancjami są określane w czasie wykonywania, na przykład przy użyciu ładowania dynamicznego;
  • chcesz uniknąć budowania hierarchii klas lub fabryk, które są równoległe do hierarchii klas produktów;
  • instancje klasy mogą znajdować się w jednym z kilku różnych stanów. Bardziej wygodne może być ustawienie odpowiedniej liczby prototypów i sklonowanie ich, zamiast ręcznego tworzenia instancji klasy w odpowiednim stanie za każdym razem.