Observer Design Pattern kilka pytań

0

Ostatnio próbuję swoich sił w refactoringu kodu projektu, z którym borykam się na co dzień. Mam niewielkie doświadczenie (7msc), więc proszę to wziąć pod uwagę gdybyście chcieli coś wytłumaczyć. Słowem wstępu przedstawię klasy biorące udział w rozważaniach i pożądane zachowanie.

SubjctToOrder - klasa reprezentująca powiązanie pomiędzy klientem, a przedmiotem zamówienia rozumianym u nas w systemie jako nieruchomość
Realestate - model reprezentujący nieruchomość
Contact - model reprezentujący klienta
Wall - na wallu są wyświetlane m.in. różne powiadomienia, takie jak zmiana statusy powiązania itp.

W momencie **zmiany statusu **powiązania (SubjctToOrder) powinien zostać ustawiony odpowiedni status klienta (Contact), odpowiedni status nieruchomości (Realestate) oraz dodany post na wallu (Wall).

W momencie usunięcia powiązania sytuacja wygląda trochę inaczej. To co się rożni to ustawiana jest flaga deleted na 1 (SubjectToOrder) oraz jest dodawany inny post na wallu informujący o usunięciu powiązania. Na ten moment dla każdego komunikatu jest klejona oddzielna metoda w modelu Wall. Akcje w Concat i Realestate pozostają takie same, tylko wywoływana jest zmiana statusy na dostępny, ale wciąż jest to zmiana.

Obecnie wszystko jest robione w zakręconej metodzie postSave. W momencie usunięcia wywoływana jest metoda softDelete, która w zasadzie wygląda identycznie jak postSave (co swoją drogą generuje 150 linii zduplikowanego kodu, łamie zasadę SRP i wiele innych).

Jak tak chwilę na to popatrzyłem to przypasował mi do tego Observer Pattern, ale w nim występuje jedna metoda update i po chwili jednak bardziej byłem skłonny ku zaimplementowaniu Event Managera typu SubjectToOrderStatusChanged i SubjectToOrderDeleted.

Pytania:

  1. Jeden ze starszych programistów w odpowiedzi na mój pomysł stwierdził, że wpychanie tutaj Observera znacząco utrudnia debuggowanie i lepiej tego nie robić. Hmm.. jak ktoś używa var_dumpa, zamiast debuggera.. no nie wiem, co o tym myślicie?

  2. Czy powinienem zaimplementować interface Subject i Observer odpowiednio w modelach wyżej wymienionych, czy do tego tworzy się oddzielne klasy i korzystając ze Strategii wywołuje się metody tych modeli zmieniające status w odpowiednich modelach?

  3. Czy faktycznie lepszym rozwiązaniem w obecnej sytuacji będzie oparcie tego na eventach niż na Observer Pattern?

Jestem otwarty na inne pomysły, bo chcę jakoś opanować ten projekt. Na dzień dzisiejszy problemów podobnych do opisanego jest mnóstwo. Konieczne jest wywołanie tysiąca akcji np. w momencie usunięcia transakcji, a wszystko jest robione manualnie w różnych miejscach przez co jest duży bałagan.

Dziękuję za poświęcony czas.

0

Może opisałeś to trochę niedokładnie, ale dla mnie to wygląda trochę dziwnie co chcesz zrobić.
Zarówno Observer jak i Eventy nadają się do sytuacji kiedy pewna akcja ma powodować kaskadę (potencjalnie asynchronicznych przy eventach) akcji w różnych częściach systemu. W szczególnosci w miejscach które nie są znane -> siła Observera i Event Busa jest to że rejestrować się mogą dowolne obiekty i ten kto wysyła notyfikacje o zmianie nie musi w ogóle wiedzieć o tym kto "słucha".

Ale u ciebie nie widzę w ogóle takiej sytuacji. U ciebie widzę że wykonanie akcji ma powodować wywołanie metod z jakiegoś Serwisu który zmodyfikuje Model.

0

Hmm, masz rację. Czyli jest to poprawnie zaimplementowane? Czy jesteś może w stanie stwierdzić z powyższego opisu jak można to usprawnić i czy jest to konieczne?

Jakoś mi nie pasowało to, że to wszystko się przenika i jest upchane w jednej metodzie pod nazwą postSave. Czyli np. ma być wykonane X czynności po zmianie statusu i wszystkie są tam wrzucone do jednego wora. Logika zmiany statusów, usuwanie dokumentów powiązanych, posty na wallu, to wszystko siedzi w postSavie i to nie na zasadzie takiej, że są tam wywoływane metody, które to realizują tylko wpieprzone są ify, tworzenie odpowiednich klas potrzebnych do wykonania danego zadania itp.

W zasadzie nie jesteś w stanie stwierdzić co się dzieje po zmianie statusu bez przebrnięcia przez to spaghetti

1

Ale to problem jest zupełnie inny i nazywa się dekompozycja :) Trzeba tą metodę rozbić na kawałki, może nawet wydzielić kilka małych serwisów, może opartych o Strategie. Niemniej na pewno zostawiłbym tutaj model "push" a nie kombinował z "pull" przez observera czy eventy.

0

Bomba. Póki co wiem, że dzwonią, ale jeszcze nie wiem gdzie, to co napsałeś bardzo pomogło :)

1
Shalom napisał(a):

Niemniej na pewno zostawiłbym tutaj model "push" a nie kombinował z "pull" przez observera czy eventy.

"pull" byłby bardzo fajny gdyby to były mikroserwisy, coś co zmienia status wysyła notyfikacje a potem coś te notyfikacje konsumuje.

Tutaj rozumiem że jest to monolit, masz giga wielki model i jakiś elseniorrodeveloperro wpadł na pomysł żeby całą logike wrzucić do tego twojego postSave (piszesz w PHP ?) :D:D:D

Problemem jest to że nie powinien to być żaden postSave tylko serwis(w sumie to widzę że nic nowego nie napiszę), a raczej parę serwisów, a to spięte facadą(nudnie).

  1. Jeden ze starszych programistów w odpowiedzi na mój pomysł stwierdził, że wpychanie tutaj Observera znacząco utrudnia debuggowanie i lepiej tego nie robić

To jest akurat biedny argument, to że intellij nie jest sobie w stanie poradzić a asynchronicznymi wywołaniami ma znaczyć nie wszystko powinno iść w jednym wywołaniu ? nie, noo, zamieniasz jeden ból na inny.

  1. Czy powinienem zaimplementować interface Subject i Observer odpowiednio w modelach wyżej wymienionych, czy do tego tworzy się oddzielne klasy i korzystając ze Strategii wywołuje się metody tych modeli zmieniające status w odpowiednich modelach?

overengineering :D Taka (nie lubie tego słowa) logika biznesowa powinna być wykonywana jawnie, potem przyjdzie ktoś nowy i się będzie pół dnia zastanawiał a czemu takie rzeczy się pod spodem dzieją.

W takim typoeo ERPo landzie, sklepałbym to jakoś tak

class OrderManagmentFacade
{
	updateStatus(AffectedState affectedState){}
}

class AffectedState {
	Realestate realestate;
	Contact contact;
	Wall wall;
}

class Realestate {}

class Contact {}

class Wall {}

I ja domyślam się że chcesz zmienić pół systemu bo Ci się nie podoba (nie hejtuje, taki zapał jest na +), ale takie wprowadzanie jakiś event-busow do istniejącego i dużego systemu to zabójstwo. Zacznij pisać testy, dziel syf na mniejsze serwisy, wymyślaj dobre nazwy - to ważniejsze.

P.S

W zasadzie nie jesteś w stanie stwierdzić co się dzieje po zmianie statusu bez przebrnięcia przez to spaghetti

A byłbyś w stanie to sprawdzić jakby taka zmiana szła jako notyfikacja ? zresztą, co by się stało jakby na takie zdarzenie słuchało dwóch listenerów(dwa listenery ?), to który zapdejtuje dane jest dość niedeterministyczne, mogło by się w twoim systemie wydarzyć coś takiego że ktoś usuwa te połączenie(zdarzenie1 przez listener1), potem ktoś znowu je nawiązuje, ale zdarzenie wysłane jako pierwsze usunięcie zostało obsłużone z opóźnieniem(zdarzenie1 przez listener2) i tak naprawdę zostaje wykonane niejako dwa razy ? wiem że to może brzmieć dość poyebanie. Dodając takie kul ficzery trzeba pamiętać o konsekwencjach.

P.S.2 moje wypowiedzi musisz traktować z większym dystansem, nie mam studiów i słabo całkuje

0

masz giga wielki model i jakiś elseniorrodeveloperro wpadł na pomysł żeby całą logike wrzucić do tego twojego postSave

Tak, php :)

I ja domyślam się że chcesz zmienić pół systemu bo Ci się nie podoba

To nie tylko chodzi o podobanie. Codziennie tracę kilka h na naprawianie jakiegoś buga, który wynika z tego, że jest taki bajzel i brakuje testów. Naprawię jedną rzecz, to popsuje 10 innych, a o kolejnych 10 dowiem się od klienta. Klasy i metody działają na zasadzie dźwigni w jakiś trybachif($mode == "pdf") elseif ($mode == "jpg") itp. To wziąłem książkę do wzorców i refactoringu, przeczytałem i staram się coś poprawić, bo inni już chyba zwątpili :D

Dzięki za cenne uwagi.

1 użytkowników online, w tym zalogowanych: 0, gości: 1