Wzorce projektowe: Pyłek(Flyweight)

Czas na kolejny wzorzec, a dokładniej na wzorzec Pyłek(Flyweight), którego celem jest w dużym skrócie ograniczenie zajmowanej pamięci przez wiele obiektów, dalej o wzrocu w artykule.

 

Cel

  • Jak największe zmniejszenie wykorzystywanej pamięci zmarnowanej na obsługę wielu podobnych obiektów.
  • Zastępowanie tzw ciężkich obiektów, obiektami lekkimi.
  • Wykorzystanie współużytkowania obiektów do efektywnego zarządzania wieloma obiektami, czyli nie tworzymy każdego obiektu od początku tylko bazujemy na już utworzonych obiektach dzięki temu zwiekszamy szybkość aplikacji.

 

Problem

Wzorzec Pyłek jest np wykorzystywany w przeglądarkach jako cache, czyli jeśli np użytkownik wejdzie na jakąś stronę to ona wczytuje się od zera jednak jeśli użytkownik znowu wejdzie na tą samą stronę to wczyta się ona szybciej, ponieważ powtarzające się elementy strony np tekst, obrazki zostały za pierwszym wyświetleniem strony zapisane do pamięci podręcznej przeglądarki, więc przeglądarka nie musi znowu wczytywać elemntów strony od zera tylko z pamięci podręcznej w której są już zapisane elementy strony 🙂

Innym przykładem wykorzystania, jest np dokument tekstowy, w którym załóżmy że jest z tysiąc znaków tekstowych to każdy z tych znaków nie jest tworzony od zera np jeśli chcemy wpisać jakiś znak, to sprawdzamy czy taki sam znak jest już w tym dokumencie i tworzymy jego obiekt lekki wcześniejszej kopii dzięki temu wszystko działa szybciej i mamy do wykorzystania więcej pamięci.

 

Użyj wtedy kiedy:

  • Tworzysz aplikację, która korzysta z dużej liczby powtarzających się obiektów.
  • Twoje obiekty są kosztowne w przechowywaniu.
  • Stan obiektu, który tworzysz można zapisać poza nim, czyli np kiedy masz 100 obiektów i w każdym jest pole np kolor to wtedy możesz rozwiązać to w ten sposób, że tworzysz taki jeden obiekt(lub więcej jeśli są w nich np cztery typy kolorów🌈) od zera i danymi o kolorze dzielisz się z pozostałymi 99 obiektami jest to dużą oszczędność pamieci oraz zwiększenie wydajności, ponieważ nie musimy za każdym razem tworzyć tych obiektów od zera.
  • W twojej aplikacji identyczność obiektów nie ma znaczenia, czyli wszelkie operacje będzie można wykonywać na kopiach tych obiektów.

 

Struktura

Poniżej jest diagram UML wzorca Pyłek, który pokazuje pare ciekawych rzeczy, miedzy innymi, że często z Pyłkiem łączy się wzorzec fabryki abstrkcyjnej, np sprawdzamy czy jakiś obiekt jest w dokumencie jeśli tak to tworzymy instancje tego obiektu, która jest wydzielona do fabryki abstrakcyjnej. Widzimy też, że obiekt jest tworzony wtedy kiedy tego potrzebujemy, nie tworzymy wszystkich obiektów odrazu, oraz ,że fabryka pełni funkcję cachowania czyli sprawdza czy są te same obiekty w aplikacji, jeśli są to tworzy kolejne podobne obiekty na podstawie danych innych obiektów zapisanych w fabryce.

Flyweight scheme

Zobaczmy taki przykład:

Flyweight scheme

Klasy Locust, Ant oraz Cockroach mogą być zastąpione klasami lekkimi, ponieważ nie są zahermetyzowane i ich część musi być uzupełniona przez klienta oraz są do siebie podobne tutaj znowu fabryka pełni funkcje cachowania.

 

 

Dwa przykłady z życia wzięte

Zapisywanie znaków w dokumencie

Zobaczmy jak mniej więcej w praktyce wygląda, a raczej powinno wyglądać zapisywanie znaków w dokumencie np txt, zacznijmy od abstrakcyjnej klasy głównej.

Do klasy abstrakcyjnej Char zapisujemy znaki oraz klasy które po niej dziedziczą to właśnie nimi tworzymy obiekty znaków, może się wydawać, że jest ich dużo, ale tak naprawdę jest ich bardzo mało w porównaniu z tym ile znaków może być w dokumencie czy łańcuchu, mamy również stan wewnątrzny w klasie Char, którym jest zmienna znaków do której znaki zapisujemy i je wyświetlamy. Jest ona poniżej:

A teraz zobaczmy co dzieję się w klasie  fabryki wzorca Pyłek.

W metodzie której przekazujemy znaki czyli GetChar sprawdzamy czy w słowniku, którego zdefiniowalismy na początku klasy nie ma tego samego znaku jesli jest, to jedynie zapisujemy znak do zmiennej letter żeby go wyświetlić, ale nie tworzymy ponownie obiektu tego znaku i na tym właśnie polega wzorzec pyłek, że nie tworzymy dwa razy tego samego obiektu i dzielimy się danymi. Tak wygląda klasa fabryki:

Następnie jeśli nie ma takiego znaku w słowniku, to musimy utworzyć jego obiekt lekki, zapisać go do słownika, żeby później nie musieć tworzyć dwa razy tego samego obiektu i zapisujemy go do zmiennej letter, żeby go wyświetlić. A klasy obiektów lekkich , czyli w naszym przypadku znaków, wyglądają tak:

Może ci się nie podobać sposób w jaki tworzymy te obiekty, bo tworzymy je za pomocą fabryki prostej, ale akurat takie zastosowanie fabryki jest właściwe bo zauważ, że jest ograniczona liczba typów znaków w angielskim alfabacie są tylko 24 znaki, a na pewno tyle typów znaków będzie w dużym dokumencie no i musimy wyświetlić te znaki automatycznie nie możemy wybierać sobie jakie znaki chcemy wyświetlić bo to by nam zajęło w większym projekcie ze sto lat. Czyli takie zastosowanie fabryki do takiego problemu jest w porządku🙂

Na koniec klient:

Po stronie klienta ustawiamy kolor, czyli definiujemy kolor poza stanem obiektu, żeby oszczędzić pamięć i wydajność. Oraz tworzymy obiekt fabryki do której w pętli wkładamy po kolei znaki łańcucha, który zdefiniowaliśmy na początku i wyświetlamy ten łańcuch

Wynik jest taki:

Powtarzające się znaki to E, T, więc nie tworzyliśmy drugi raz ich obiektów. W dużej aplikacji to byłaby duża oszczędność pamięci i wydajności.

 

Prototyp cache

W tym przykładzie zrobimy sobie prototyp cachowania, który działa w przeglądarkach, na przykładzie obrazków, będziemy wczytywać całe z pierwszym razem, ale już z następnymi akcjami będziemy wczytywać obrazki za pomocą ich zapisanych parametrów. Wstawię jakiś obrazek, który znalałem w sieci żeby można było to łatwiej zrozumieć.

Flyweight scheme

A teraz kod:

Tak wygląda cache w naszym wykonaniu,  zapisujemy w cache nazwy obrazków ich formaty, nazwy obrazów z formatami mogą czasem się powtarzać, kod wygląda podobnie, jak w poprzednim przykładzie.

Za pierwszym wyświetleniem strony zapisujemy nazwy obrazków do słownika i ich obiekty i za pomocą pętli foreach wyświetlamy je w kliencie, za drugiem wyświetleniem strony mamy już zapisane nazwy w słowniku, wystarczy je wyświetlić. W fabryce pyłka oddzielamy nazwę obrazka od jego formatu za pomocą regexa. Reszta wygląda podobnie jak w poprzednim przykładzie.

Najpierw wyświetlamy wszystkie obrazki, następnie wyświeltamy obrazki które zostały zapisane w słowniku czyli można powiedzieć w cache, oraz wyświetlamy liczbę obrazków, które mają takie same nazwy i formaty w naszym przykładzie jest jeden obrazek, który powtarza się dwa razy.

Wynik jest taki:

 

Mały update o wielowątkowości w pyłku

Uwaga mały update! Opublikowałem, ten wpis wczoraj, ale zapomniałem wspomnieć o również ważnej rzeczy w pyłku, czyli o wielowątkowości, jako, że w pyłku możemy mieć paru klientów, którzy mogą używać tego samego obiektu, czyli klasy pyłka muszą być zaimplementowane w sposób bezpieczny lub być obiektami typu immutable(czyli pole ze słowem kluczowym readonly) czyli niezmienne. A klasy pyłka są singletonami, w lekcji o singletonie na blogu pisałem, że klasy typu singleton powinny a wręcz muszą być zabezpieczone z wielu wątków, dlatego, że takie klasy mogą mieć tylko jedną instancję.

Ale trzeba mieć więdzę o wątkach, zadaniach, wielowątkowości o tym wszystkim pisałem w dwóch ostatnich artykułach o C#, w artykułach o współbieżności i  asynchroniczności. Po ich przeczytaniu wszystko stanie się jasne🙂.

Ale pamiętaj, ze wzorcami jest podobnie jak z zasadmi solid, sztywne trzymanie się reguł implementacji w większym projekcie, żeby zaimplementować wszystko idealnie bardzo rzadko jest możliwe prawie nigdy, zawsze dostosowywuj wzorzec pod swoje potrzeby.

Czyli nie postępuj tak jak ten Pan poniżej🙂

 

 

Pyłek(Flyweight) vs Prototyp(Prototype)

Pyłek jest dosyć podobny do wzorca prototyp jednak nieco różnią się od siebie, między innymi:

  • Pyłek jest wzorcem strukturalnym a prototyp konstrukcyjnym
  • We wzorcu prototyp po prostu klonujemy wielokrotnie jeden obiekt, a we wzorcu Pyłek wykorzystujemy utworzone obiekty ponownie poprzez dzielenie się nimi, nowe obiekty są tworzone tylko wtedy kiedy nie będzie takiego lub podobnego obiektu w aplikacji.
  • Prototyp jest do tworzenia nowych instancji lub ich klonowania, Pyłek jest do tworzenia i dzielenia się nimi.

 

Relacje z innymi wzorcami projektowymi

  • Pyłek(Flyweight) jest często łączony z kompozytem w celu zaimplementowania wspólne gałęzie liści.
  • Gdzie Pyłek pokazuje jak utworzyć wiele małych obiektów, Fasada pokazuje jak utworzyć jeden obiekt, który będzie zarządzał całym systemem.
  • Składnię z Interpretera można udostępnić pyłkowi.
  • Stan i Strategia są często zaimplementowane jak pyłki.
  • Obiekty Pyłka są często singletonami, ponieważ może być tylko jeden taki sam obiekt.

 

Podsumowanie

The end🙂

Ponizej link do githuba ze wszystkim przykładami z tego artykułu.

link: https://github.com/Slaw145/FlyweightTutorial

I to był ostatni artykuł o wzorcach strukturalnych, następna lekcja będzie o wzorcu Object pool. Podobnym do Pyłka, ale jest wzorcem konstrukcyjnym, ale wolałem go teraz opisać bo oba mają podobne cele. A w następnym artykule po wzorcu Object pool będzie o wzorcu operacyjnym Interpreter. Czyli trochę plany się zmieniły 🙂

Oczywiście jak zwykle przypominam o newsletterze, żeby nie przegapić kolejnych wpisów, oraz o grupie i stronie devman na facebooku.

P.S W niedalekiej przyszłości zamierzam wysyłać newsletterem ciekawostki ze świata IT, nie tylko te, które publikuje na grupie oraz stronie na fb, ale także narzędzia oraz wskazówki programistyczne, które mogą ci się przydać🙂

Do zobaczenia🙂

 
Jeśli ten wpis ci się przydał podziel się nim ze swoimi znajomymi :)

Post a comment

avatar
  Subscribe  
Notify about