wzorce projektowe są rozwiązaniami na poziomie projektowania dla powtarzających się problemów, z którymi my inżynierowie oprogramowania często się spotykamy. To nie jest kod – powtarzam, kod. To jest jak opis, jak rozwiązać te problemy i zaprojektować rozwiązanie.
Korzystanie z tych wzorców jest uważane za dobrą praktykę, ponieważ projekt rozwiązania jest dość wypróbowany i przetestowany, co skutkuje większą czytelnością końcowego kodu., Wzorce projektowe są dość często tworzone i używane przez Języki OOP, takie jak Java, w których zostanie napisana większość przykładów.
rodzaje wzorców projektowych
obecnie odkryto około 26 wzorców (chyba nie zrobię ich wszystkich…).
te 26 można podzielić na 3 typy:
1. Creational: te wzorce są przeznaczone do tworzenia instancji klas. Mogą to być zarówno wzorce tworzenia klas, jak i wzorce tworzenia obiektów.
2. Strukturalne: te wzory są zaprojektowane z uwzględnieniem struktury i składu klasy., Głównym celem większości z tych wzorów jest zwiększenie funkcjonalności danej klasy, bez zmiany jej składu.
3. Behawioralne: te wzorce są projektowane w zależności od tego, jak jedna klasa komunikuje się z innymi.
w tym poście omówimy jeden podstawowy wzór projektowy dla każdego sklasyfikowanego typu.
Type 1: Creational – wzorzec projektowy Singleton
wzorzec projektowy Singleton jest Wzorzem projektowym, którego celem jest stworzenie tylko jednej instancji klasy i zapewnienie tylko jednego globalnego punktu dostępu do tego obiektu., Jednym z najczęściej używanych przykładów takiej klasy w Javie jest Calendar, gdzie nie można utworzyć instancji tej klasy. Używa również własnej metody getInstance()
, aby uzyskać obiekt do użycia.
Klasa używająca wzorca singleton będzie zawierać
- prywatną zmienną statyczną, trzymającą jedyną instancję klasy.
- prywatny konstruktor, więc nie można go utworzyć nigdzie indziej.
- publiczna statyczna metoda zwracająca pojedynczą instancję klasy.,
istnieje wiele różnych implementacji projektu singleton. Dzisiaj przejrzę implementacje;
1. Eager Instantiation
2. Lazy Instantiation
3. Thread-safe Instantiation
Eager Beaver
public class EagerSingleton {// create an instance of the class.private static EagerSingleton instance = new EagerSingleton();// private constructor, so it cannot be instantiated outside this class.private EagerSingleton() { }// get the only instance of the object created.public static EagerSingleton getInstance() {return instance;}}
ten typ instantiation ma miejsce podczas ładowania klasy, ponieważ instantiation zmiennej występuje poza dowolną metodą. Stanowi to ogromną wadę, jeśli ta klasa nie jest w ogóle używana przez aplikację kliencką. Plan awaryjny, jeśli ta klasa nie jest używana, to Lazy Instantiation.,
leniwe dni
nie ma dużej różnicy w stosunku do powyższej implementacji. Główną różnicą jest to, że zmienna statyczna jest początkowo zadeklarowana jako null i jest tworzona tylko w ramach metody getInstance()
wtedy i tylko wtedy, gdy zmienna instancji pozostaje null w czasie sprawdzania.
To rozwiązuje jeden problem, ale inny nadal istnieje. Co jeśli dwóch różnych klientów uzyska dostęp do klasy Singleton w tym samym czasie, aż do milisekundy?, Cóż, sprawdzą, czy instancja jest null w tym samym czasie i uznają to za prawdziwe, a więc stworzą dwie instancje klasy dla każdego żądania obu klientów. Aby to naprawić, należy zaimplementować bezpieczną instancję wątku.
(Thread) Safety is Key
w Javie słowo kluczowe synchronized jest używane w metodach lub obiektach do implementacji bezpieczeństwa wątku, tak że tylko jeden wątek będzie miał dostęp do określonego zasobu w tym samym czasie. Instancjacja klasy jest umieszczana w zsynchronizowanym bloku tak, że metoda może być dostępna tylko dla jednego klienta w danym czasie.,
narzut dla metody zsynchronizowanej jest wysoki i zmniejsza wydajność całej operacji.
na przykład, jeśli zmienna instancji została już utworzona, to za każdym razem, gdy Klient uzyskuje dostęp do metody getInstance()
, metoda synchronized
jest uruchamiana i wydajność spada. Dzieje się tak tylko po to, aby sprawdzić, czy wartość zmiennych instance
jest null. Jeśli stwierdzi, że tak jest, opuszcza metodę.
w celu zmniejszenia tego obciążenia stosuje się podwójne blokowanie., Sprawdzanie jest również używane przed metodą synchronized
I jeśli wartość jest równa null, to metodasynchronized
jest uruchamiana.
teraz do następnej klasyfikacji.
Type 2: Structural – the Decorator Design Pattern
dam ci mały scenariusz, aby dać lepszy kontekst dlaczego i gdzie powinieneś użyć wzoru dekoratora.
powiedzmy, że posiadasz kawiarnię i jak każdy początkujący zaczynasz od dwóch rodzajów zwykłej kawy, the house blend i dark roast., W systemie rozliczeniowym istniała jedna klasa dla różnych mieszanek kawy, która dziedziczy klasę beverage abstract. Ludzie rzeczywiście zaczynają przychodzić i mieć swój wspaniały (choć gorzki?) kawa. Są też kawowe nowinki, które, broń Boże, chcą cukru lub mleka. Co za parodia na kawę!! ??
teraz musisz mieć te dwa dodatki, zarówno do menu, jak i niestety w systemie rozliczeniowym. Pierwotnie twoja osoba z branży IT stworzy podklasę dla obu kaw, jednej z cukrem, a drugiej mlekiem., Następnie, ponieważ klienci mają zawsze rację, jeden mówi te przerażające słowa:
„mogę dostać kawę mleczną, z cukrem, proszę?”
???
znowu Twój system rozliczeniowy śmieje ci się w twarz. Wracając do deski kreślarskiej….
informatyk następnie dodaje kawę mleczną z cukrem jako kolejną podklasę do każdej klasy kawy rodzica. Reszta miesiąca to płynne żeglowanie, ludzie ustawiają się w kolejce po kawę, a Ty zarabiasz pieniądze. ??
ale czekaj, to nie wszystko!
świat znów jest przeciwko tobie., Konkurent otwiera się po drugiej stronie ulicy, z nie tylko 4 rodzaje kawy, ale więcej niż 10 dodatków, jak również! ?
kupujesz te wszystkie i więcej, aby samemu sprzedawać lepszą kawę, a potem pamiętaj, że zapomniałeś zaktualizować ten drastyczny system rozliczeniowy. Całkiem możliwe, że nie możesz stworzyć nieskończonej liczby podklas dla dowolnych kombinacji wszystkich dodatków, z nowymi mieszankami kawy. Nie wspominając o wielkości systemu końcowego.??
czas zainwestować w odpowiedni system rozliczeniowy., Znajdujesz nowych pracowników IT, którzy faktycznie wiedzą, co robią i mówią;
„Dlaczego, będzie to o wiele łatwiejsze i mniejsze, jeśli użyje wzoru dekoratora.”
Co to jest?
wzorzec projektowy dekoratora należy do kategorii strukturalnej, która zajmuje się rzeczywistą strukturą klasy, niezależnie od tego, czy jest dziedziczona, składowa, czy obie. Celem tego projektu jest modyfikacja funkcjonalności obiektu w czasie wykonywania. Jest to jeden z wielu innych wzorców projektowych, które wykorzystują abstrakcyjne klasy i interfejsy z kompozycją, aby uzyskać pożądany rezultat.,
dajmy matmie szansę (drżeć?), aby to wszystko ujrzeć w perspektywie;
weź 4 mieszanki kawy i 10 dodatków. Jeśli trzymamy się generowania podklas dla każdej innej kombinacji wszystkich dodatków dla jednego rodzaju kawy. To jest;
(10-1) 2 = 92 = 81 podklasy
odejmujemy 1 od 10, ponieważ nie można łączyć jednego dodatku z drugim tego samego typu, cukier z cukrem brzmi głupio. I to tylko za jedną mieszankę kawy. Pomnóż to 81 przez 4, a otrzymasz aż 324 różne podklasy!, Mów o tym wszystkim kodowanie …
ale ze wzorem dekoratora będzie wymagać tylko 16 klas w tym scenariuszu. Chcesz się założyć?
Jeśli odwzorujemy nasz scenariusz zgodnie z powyższym schematem klas, otrzymamy 4 klasy dla 4 mieszanek kawy, 10 dla każdego dodatku i 1 dla komponentu abstrakcyjnego i 1 więcej dla dekoratora abstrakcyjnego. Widzisz! 16!, A teraz oddaj te 100 dolarów.?? (jk, ale to nie będzie odrzucone, jeśli podane… tylko mówię)
jak widać z góry, tak jak konkretne mieszanki kawy są podklasami klasy abstrakcyjnej napoju, klasa abstrakcyjna AddOn również dziedziczy swoje metody z niej. Dodatki, które są jego podklasami, z kolei dziedziczą wszelkie nowe metody, aby w razie potrzeby dodać funkcjonalność do obiektu podstawowego.
przejdźmy do kodowania, aby zobaczyć ten wzór w użyciu.,
najpierw należy utworzyć klasę Abstract beverage, od której odziedziczą się wszystkie różne mieszanki kawy:
następnie dodać obie klasy concrete coffee blend.
klasa abstrakcyjna addon dziedziczy również z klasy abstrakcyjnej Beverage (więcej na ten temat poniżej).
a teraz konkretne implementacje tej abstrakcyjnej klasy:
Jak widać powyżej, możemy przekazać dowolną podklasę napoju do dowolnej podklasy AddOn i uzyskać dodany koszt oraz zaktualizowany opis. A ponieważ Klasa AddOn jest zasadniczo typu napój, możemy przekazać dodatek do innego dodatku., W ten sposób możemy dodać dowolną liczbę dodatków do konkretnej mieszanki kawy.
teraz napiszcie jakiś kod aby to przetestować.
ostateczny wynik to:
to działa! Udało nam się dodać więcej niż jeden dodatek do mieszanki kawy i pomyślnie zaktualizować jej ostateczny koszt i opis, bez konieczności tworzenia nieskończonych podklas dla każdej kombinacji dodatków dla wszystkich mieszanek kawy.
wreszcie do ostatniej kategorii.,
Typ 3: behawioralny – wzorzec projektowania poleceń
behawioralny wzorzec projektowy skupia się na tym, jak klasy i Obiekty komunikują się ze sobą. Głównym celem wzorca dowodzenia jest wpajanie wyższego stopnia luźnego sprzężenia między zaangażowanymi stronami (Czytaj: klasy).
Uhhhh… Co to jest?
sprzęganie jest sposobem, w jaki dwie (lub więcej) klasy, które oddziałują ze sobą, cóż, współdziałają. Idealnym scenariuszem, gdy te klasy współdziałają, jest to, że nie zależą one w dużym stopniu od siebie. To luźne połączenie., Tak więc, lepszą definicją dla luźnego sprzężenia byłoby, klasy, które są ze sobą połączone, czyniąc najmniej wykorzystanie siebie nawzajem.
potrzeba tego wzorca powstała, gdy żądania musiały być wysyłane bez świadomej wiedzy, o co prosisz lub kto jest odbiorcą.
w tym wzorze Klasa wywołująca jest oddzielona od klasy, która faktycznie wykonuje akcję. Klasa invoker posiada tylko wywoływalną metodę execute, która uruchamia niezbędne polecenie, gdy klient je zażąda.
weźmy podstawowy przykład z prawdziwego świata, zamawiając posiłek w eleganckiej restauracji., W miarę upływu czasu, przekazujesz swoje zamówienie (polecenie) kelnerowi( invokerowi), który następnie przekazuje je kucharzowi(odbiorcy), abyś mógł zdobyć jedzenie. Może to brzmi prosto… ale trochę jak kod.
pomysł jest dość prosty, ale kodowanie krąży wokół nosa.,
przepływ operacji po stronie technicznej jest, tworzysz konkretne polecenie, które implementuje interfejs poleceń, prosząc odbiorcę o wykonanie akcji i wysyłając polecenie do wywoływacza. Wywoływacz jest osobą, która wie, kiedy wydać to polecenie. Kucharz jest jedynym, który wie, co zrobić, gdy otrzyma konkretne polecenie / rozkaz., Tak więc, gdy uruchomiona jest metoda execute wywoływacza, to z kolei powoduje uruchomienie metody execute obiektów poleceń na odbiorniku, wykonując w ten sposób niezbędne akcje.,
to, co musimy zaimplementować to;
- polecenie interfejsu
- polecenie klasy, które implementuje interfejs polecenia
- kelner klasy (invoker)
- kucharz klasy (odbiornik)
Tak więc kodowanie przebiega następująco:
kucharz, odbiornik
polecenie, interfejs
public interface Command {public abstract void execute();}
zamówienie, konkretne polecenie
kelner, Fakturujący
public class Waiter {private Order order;public Waiter(Order ord) {this.order = ord;}public void execute() {this.order.execute();}}
ty, klient
jak widać powyżej, Klient składa zamówienie i ustawia odbiorcę jako kucharza., Zamówienie jest wysyłane do kelnera, który będzie wiedział, kiedy wykonać zamówienie (tj. kiedy przekazać szefowi kuchni zamówienie na gotowanie). Gdy Wywoływacz jest wykonywany, na odbiorniku uruchamiana jest metoda execute zleceń (tzn. kucharz otrzymuje polecenie gotować makaron? albo upiec ciasto?).,
Szybkie podsumowanie
w tym poście przejrzeliśmy:
- czym tak naprawdę jest wzór projektowy,
- różne rodzaje wzorców projektowych i dlaczego są one różne
- jeden podstawowy lub wspólny wzorzec projektowy dla każdego typu
Mam nadzieję, że to było pomocne.
Znajdź kod repo dla posta, tutaj.