Wzorzec Listener albo Delegate

0

Czy jest jakiś dominujący wzorzec jakim w Javie by się można było się subskrybowac z Listenerów na zmiany w obiekcie jakiejś klasy?

Przykład zastosowania: wysoce czysty MVC, i klasa Model nie wie o swoich Kontrolerach / Widokach, ale one (w ilości nieznanej, większej niż jeden) by się chciały zapisać na eventy o zmianie danych w Modelu
NIE MÓWIĘ o eventach i Listenerach w pakietach GUI, ani nie jestem zainteresowany ciężkimi kontenerami czy czymś podobnym. Java SE.

W post-Java8 lambdach i rozszerzeniach funcyjnych jest coś?

Jak eventy i delegaty w C# (oczywiście, syntax mamy jaki mamy, będzie więcej boileplate, setery Modelu będą nieco bardziej rozbudowane, to jasne)

public delegate void Notify();  // delegate
                    
public class ProcessBusinessLogic
{
    public event Notify ProcessCompleted; // event

}
0

Nie wiem co tam dokładnie jest, ale jeśli to co zmienia model, może określić tą zmianę, to możesz użyć wzorca Observer. — malencki dziś, 01:30

Ale masz jakaś typową, generyczną implementację tego Observera na klasie (prawie) POJO ?
Listener, Observer ... dyskusja o słowach, jedno bardziej od teorii, drugie od praktyki kodowania. To niemal to samo. Mówimy partia, myślimy Lenin

To, co zmienia Model, wie że zmienia - ale nie wie, kto jeszcze jest chętny powiadomienia (ani nawet ile tego jest)

w C# to się robi prawidłowo architektonicznie: przedmiotowa klasa udostępnia mechanizm eventowy (a język daje sympatyczny syntax i semantykę, ale to traktuję zaledwie jak sugar, kij że w Javie byłby nieco dłuższy zapis) - nie ma powodu tworzyć nieprawidłowych zależności w grafie.

Wszyscy patrzą na panią Ziutę, Ziuta wie że potencjalnie jest obserwowana, czasem puści jakiegoś tweeta, ale żaden konkretny adorator jej nie obchodzi.
A Ty kierujesz uwagę na Kazka, on spowodował ostatni tweet.

0

Co powiecie na PropertyChangeSupport
https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/java/beans/PropertyChangeSupport.html
https://www.baeldung.com/java-observer-pattern

jest tym "wiodącym rozwiązaniem", przyjętym przez rynek (znów porównanie do C#), czy tylko samotnie wspierana przez Swinga ?
Co poeta miał na myśl, ze dał Serializable ?

0

Jeżeli masz klasę na której chcesz nasłuchiwać zmian i powiadamiać, to sposobów jest kilka. Albo czyste i upierdliwe, albo mniej czyste, ale zostaje lekki niesmak.
Najprostszy sposób, to jak już znalazłeś, dorzucenie do każdej metody zmieniającej stan klasy firePropertyChange(). Tylko trzeba to dorzucić niestety sporo kodu do tej klasy, albo wewnątrz niej, albo jakoś ją owinąć (delegate).
Nasłuchiwanie jest już proste. Piszesz metody addPropertyChangeListener(), removePropertyChangeListener() i zrobione.

Można sobie zaoszczędzić roboty z pisaniem delegatów, korzystając z https://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html Czyli taki uniwersalny delegate oparty na refleksjach.

0

@ZrobieDobrze:
Dostałeś odpowiedź równie precyzyjną jakie zadałeś pytanie :)
Z ciekawości dopytam, czy jest to Twój pierwszy projekt? Ewentualnie styczność z wzorcami?

Ogólnie nie wykluczam eventowego rozwiązania przez Observera (jeśli jest taka potrzeba).
Podałeś strony, na których są przykłady, które definiują szablon. W realnym projekcie to tak nie wygląda (a raczej bardzo rzadko), abyś implementował coś takiego. Wszystko zależy od konkretnych potrzeb.

Nie wiem co dokładnie (konkretnie :)), chcesz uzyskać, ale jeśli system eventowy to możesz w obserwatorze umieścić na przykład listę eventów pod konkretny model.
Dodatkowo nie wiadomo czym są encje zmiany. Czy to jest tylko informacja czy może jakieś dane, które będą gdzieś wykorzystane?

Więc doprecyzuj, wtedy będzie można myśleć :D

0
piotrpo napisał(a):

Jeżeli masz klasę na której chcesz nasłuchiwać zmian i powiadamiać, to sposobów jest kilka. Albo czyste i upierdliwe, albo mniej czyste, ale zostaje lekki niesmak.

Dokładnie. To są rzeczy, którą tzw każdy próbował, i bez zadowalającego sposobiku.

Najprostszy sposób, to jak już znalazłeś,

Uhmmmm ...

Znalazłem deprecated klasę java.util.Observable, oczywiście na nią nie będę się napalał, ale sporo fajnych tropów się pojawia jak grzebać w uzasadnieniu tej deprekacji

malencki napisał(a):

Z ciekawości dopytam, czy jest to Twój pierwszy projekt? Ewentualnie styczność z wzorcami?

Jakoś ogarniam od pewnego czasu ...

Ale prawdą jest, ze jestem wrogiem religii wzorców, która rozumiem jako przymus / obowiązek, sztywny katalog np "tylko 21" wzorców, czy stosowanych na siłę ("bo 3 wzorce musza być"). Doświadczenie raczej antyintelektuwalne niż twórcze.
Dla mnie wzorce, to jak w fundamentalnej pracy Alexandra radosne klepniecie się w czoło "o kurczę, od tylu lat wszyscy w branży robimy to podobnie, opiszmy to słowami"
Ważniejsze jest STOSOWANIE właściwej metody podejścia (dobranej do zagadnienia) niż święta wojna o słowa. Na przykład w C# jest to perfekcyjnie zaimplementowane - a nikt nie wyciąga słówka na bojowe sztandary.

1

@ZrobieDobrze: Java sama z siebie nie oferuje nic "gotowego" do powiadamiania o zmianach. Wzorce oferują po prostu proxy i observer. Czyli w nieco bardziej ludzkim języku wyglądałoby to tak:


class ObservableListWrapper<E>(private val underlyingObject: MutableList<E>) : MutableList<E> by underlyingObject {
    private val listeners = mutableListOf<ListChangedListener<E>>()


    override fun add(element: E): Boolean {
        notifyListeners(element)
        return underlyingObject.add(element)
    }

    private fun notifyListeners(element: E) {
        listeners.forEach {
            it.onElementAdded(element)
        }
    }
    
    fun addListener(listener:ListChangedListener<E>){
        listeners += listener
    }
    
    fun removeListener(listener:ListChangedListener<E>){
        listeners.remove(listener)
    }

    interface ListChangedListener<E> {
        fun <E> onElementAdded(element:E)
    }
}

I miałbyś możliwość zapakowania każdej listy w taki wrapperek, który pozwalałby na nasłuchiwanie dodawania nowych elementów. Problem w tym, że Kotlin daje fajne słówko kluczowe by, którego w Javie nie ma, więc możliwości są 2. Albo dopisujesz te dodatkowe ~100 linii głupiego kodu, pewnie w osobnej klasie ListWrapper, dziedziczysz po niej i nadpisujesz metodę add(e) + jeszcze jakieś inne, które chcesz obserwować, albo korzystasz z mechanizmu DynamicProxy, czyli tworzysz taki wrapper w runtime z wykorzystaniem refleksji.

0
piotrpo napisał(a):

@ZrobieDobrze: Java sama z siebie nie oferuje nic "gotowego" do powiadamiania o zmianach.

ZrobieDobrze napisał(a):

Znalazłem deprecated klasę java.util.Observable, ... sporo fajnych tropów się pojawia jak grzebać w uzasadnieniu tej deprekacji

No właśnie coś oferuje .... wytropiłem przez https://stackoverflow.com/a/46563116/794606
Pakiet interfejsów ten: To https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/Flow.html
rzecz jest z Javy 9, wiec relatywnie nowa.
Świeże to dla mnie, nie mam żadnych przemyśleń

I miałbyś możliwość zapakowania każdej listy w taki wrapperek, który pozwalałby na nasłuchiwanie dodawania nowych elementów. Problem w tym, że Kotlin daje fajne słówko kluczowe by, którego w Javie nie ma, więc

Nie mam negatywnych myśli o Kotlinie, jest to całkiem możliwe, jeśli da więcej.

3

@ZrobieDobrze: ja do takich rzeczy stosuję EventBusa z Guavy. Nie jest to co prawda część biblioteki standardowej Javy, ale proste w użyciu i nie wymaga żadnego frameworka.

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