Wzorce projektowe: Pula obiektów(Object Pool)

W tym artykule będzie o wzorcu konstrukcyjnym, który się nazywa Pula obiektów(Object Pool), chciałem go teraz opisać bo jest dosyć podobny do wzorca Pyłek(Flyweight).

 

Cel

  • Trzymanie zainicjowanych obiektów w puli gotowych do użycia.
  • Zwrócenie obiektu na którym klient wykonywał operacje z powrotem do puli.
  • Nietworzenie wielokrotnie klas kosztownych do utworzenia, raz utworzone kosztowne obiekty są zwracane z powrotem do puli.

 

Problem

Wzorzec Pula obiektów tak jak Pyłek ma związek z cachowaniem, na zasadzie właśnie tej, że raz utworzony obiekt przez klienta, zostaje zwrócony z powrotem do puli, gdy znowu będzie klient go potrzebował znowu obiekt zostanie pobrany z puli, ale nie będzie tworzony znowu od początku bo został już wcześniej utworzony, można również w puli obiektów ustawić ilość obiektów jakie mogą być w tej puli przechowywane.

Odpowiednio wykorzystana pula może znacznie zwiekszyć wydajność aplikacji, Pula obiektów jest szczególnie użyteczna np, w połaczeniach z bazą danych albo w wątkach, ponieważ klasa Puli obiektów jest singletonem, czyli jest zabezpieczona przed dostępem z wielu wątków w jednej chwili.

Jednak działa to również w drugą stronę, niewłaściwe użycie Puli obiektów np w tworzeniu obiektów, które tylko zajmują pamięć, ale nie wskazują na inne zasoby, może znacznie zmniejszyć wydajność.

 

Użyj wtedy kiedy:

  • Musisz tworzyć obiekty kosztowne w utowrzeniu
  • Częstotoliwość tworzenia kolejnych obiektów jest również wysoka.
  • Liczba obiektów będących w użyciu jest mała.

 

Dyskusja

Trzeba pamiętać, że gdy zwracamy obiekt do puli obiektów, to jego stan musi być zresetowany, jeśli zwrócimy obiekt na którym klient wykonywał jakieś operacje, to po ponownej potrzebie wykorzystania go przez klienta, może wykonywać działania nie takie jakie byśmy chcieli. Pula obiektów, która nie resetuje stanów zwróconych obiektów jest antywzorcem. Mechanizm resetowania obiektów trzeba zaimplementować w Puli obiektów, klient nie powinien musieć resetować obiektów bo to nie klient jest odpowiedzialny za resetowanie obiektów, tylko Pula.

 

Struktura

Diagram UML wzorca Pula obiektów przedstawia wyraźnie, że jej klasa jest singletonem, ponieważ istnieje metoda getInstance(), która tworzy obiekt w klasie. Metoda acquireReusable(), która odpowiada za utworzenie obiektu oraz zapisanie go do puli, jako obiekt używany. Metoda releaseReusable(), która odpowiada za zwolnienie obiektu od klienta, i wsadzanie go do obiektów dostępnych do użycia w Puli, ale już nieużywanych, na przykładach praktycznych będzie to lepiej wyjaśnione.

I ostatnia metoda, która odpowiada za maksymalną ilość dostępnych obiektów w puli.

No i rzecz jasna klient, który wywołuje metodę getInstance(), żeby utworzyć instancję Puli obiektów. Następnie za pomocą metody acquireReusable() tworzy obiekt i zapisuje go do puli.

Object Pool scheme

 

Przykład

Poniżej przykład implementacji wzorca Pula obiektów

I tak mniej więcej wygląda schemat implementacji wzorca puli obiektów, widać, że klasa ObjectPool jest klasą generyczną, ponieważ nie zawsze będziemy wiedzieć jakie klasy będziemy chcieli zapisać w puli obiektów, a zapisujemy je w liście dzieląc je na dostępne obiekty oraz obiekty w danym momencie używane, metoda GetInstance(), tworzy obiekt klasy ObjectPool, oraz zabezpiecza przed utworzeniem dwóch pul obiektów, ponieważ ta klasa jest singletonem.

Więc tworzymy instancję obiektu objectPool w funkcji Main do którego zapisujemy klasę przykładową PooledObject. Ustawiamy maksimum obiektów jakie mogą dostępne w klasie ObjectPool na 10.

Następnie wywołujemy metodę acquireReusable(), która sprawda najpierw czy jest dostepny taki obiekt i czy jest ich mniej niż 10 jeśli tak to zapisuje pierwszy dostępny obiekt z listy do zmiennej generycznej item, oraz dodaje do obiektów używanych, no i usuwamy obiekt dostępny, bo już nie jest dostępny jest w użyciu i zmniejszamy licznik obiektów bo liczymy dostępne obiekty, nie używane obiekty.

A jeśli któryś z warunków się nie zgadza, to tworzymy instancję klasy PooledObject i zapisujemy do listy obiektów używanych. I ostatnia metoda ReleaseReusable której przekazujemy obiekt, którego nie chcemy już używać, w tej metodzie sprawdzamy czy liczniki przekroczył maksymalną wartość jeśli nie to przekazany obiekt usuwamy z używanych i zapisujemy do dostępnych i zwiększamy liczniki, ponieważ dodaliśmy kolejny obiekt do dostepnych obiektów.

I rzecz jasna gdybyśmy tworzyli coś większego powinniśmy zresetować stan zwracanego obiektu w tym przypadku nie trzeba, ale pamiętaj o tym.

Wynik:

I czy to już poprawne utworzenie klasy ObjectPool?🤔

No nie do końca bo nie zabezpieczyliśmy jej wielowątkowo, zabezpieczona wielowątkowo klasa wygląda tak jak poniżej:

Zabezpieczamy metody klasy ObjectPool słówkiem lock o którym wcześniej mówiłem w artykule o współbieżności, wywołujemy przykład z dwóch klientów, czyli dwóch wątków, jako, że mamy zabezpieczoną tą klasę wyświetli nam się komunikat na początku this is singleton, czyli i tak będziemy mieli tylko jedną instancję klasy ObjectPool, ze względu na naturę asynchroniczności wynik może wyglądać tak:

Lub tak:

 

Przykład z życia wzięty

Magazyn

Zrobimy sobie przykład na zasadzie magazynu, gdy właściciel zatrudnia pracownika, bierze z magazynu odpowiedni sprzęt i daje go pracownikowi a gdy pracownik zostanie zwolniony, sprzęt zostaje z powrotem zwrócony do magazynu, dobrze pokazuje to poniższy obrazek.

Object Pool example

A teraz zobaczmy jak to wygląda w kodzie:

Logika klasy puli obiektów pozostaje taka sama jak w poprzednich przykładach, tylko nazwy są inne. Dodaliśmy tylko dwie nowe klasy Store i Workspace.

Klasa Store korzysta z klasy puli obiektów, czyli z klasy Warehouse, która posiada funkcje zwracające obiektu do puli obiektów, ustawiające maksymalną ilość obiektów w puli itp. a klasa Workspace wyświetla tylko odpowiednie komunikaty.

I wywołanie tego wszystkiego w kliencie.

Czyli ogólnie mówiąc, procedura podobna jak poprzednio, tylko wywołania klasy ObjectPool w tym przypadku Warehouse, są w większości w klasie Store poza tym wiele się nie różni, ten przykład nie musi być zabezpieczony wielowątkowo bo zakładamy, że magazyn jest tylko jeden, oraz sklep też jest tylko jeden jednak gdybyś chciał to zrobić to wiesz jak już wcześniej podałem przykład jak to zrobić. Przeanalizuj sobie na spokojnie ten przykład.

Wynik jest taki:

 

Pyłek(Flyweight) oraz Pula obiektów(Object Pool) różnice

  • Główna różnica jest taka, że zasoby Pyłka są niezmienne czyli immutable a puli obiektów zmienne.
  • W Puli obiektów, w danej chwili obiekt może być dostepny tylko przez jednego klienta, w pyłku wielu klientów może jednocześnie używać tego samego obiektu.

 

Relacje z innymi wzorcami projektowymi

 

Podsumowanie

I to tyle o Puli obiektów i w ogóle o wzorcach konstrukcyjnych i strukturalnych w następnym artykule o wzorcu opercyjnym Interpreter. Jako, że ostatnio mi się nazbierało zajęć to w związku z tym o Interpreterze będzie wpis za dwa tygodnie, niestety życie😐

Link do githuba do wszystkich przykładów z tej lekcji:  https://github.com/Slaw145/ObjectPool

Jak zwykle dla innych głodnych wiedzy przypominam o newsletterze, żeby niczego nie przegapić i tak jak mówiłem niedługo będe wysyłał newsletterem wskazówki, narzędzia oraz nowinki z rynku IT, więc polecam oraz o stronie devman oraz grupie na facebooku.

Do zobaczenia w następnym artykule🙂

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

Post a comment

avatar
  Subscribe  
Notify about