Właśnie zaczynam od RxJava , implementacji Java ReactiveX (znanej również jako Rx i Reactive Extensions ). Coś, co naprawdę uderzyło mnie był ogromny rozmiar RxJava w płynnej klasy : ma 460 sposobów!
Aby być uczciwym:
Istnieje wiele metod, które są przeciążone, co znacznie zwiększa całkowitą liczbę metod.
Być może ta klasa powinna zostać zerwana, ale moja wiedza i rozumienie RxJava jest bardzo ograniczone. Ludzie, którzy stworzyli RxJava, są z pewnością bardzo sprytni i prawdopodobnie mogą przedstawić uzasadnione argumenty przemawiające za stworzeniem Flowalnego przy użyciu tak wielu metod.
Z drugiej strony:
RxJava to implementacja Java Reactive Extensions firmy Microsoft , która nie ma nawet klasy Flowable , więc nie jest to przypadek ślepego przenoszenia istniejącej klasy i implementacji jej w Javie.
[ Aktualizacja: Dotychczasowy punkt kursywą jest niezgodny ze stanem faktycznym: Microsoft Obserwowalne klasa, która ma ponad 400 metod, został użyty jako podstawa do RxJava za obserwowalne klasy i płynny jest podobna do Obserwowalne ale uchwyty Backpressure dla dużych ilości danych. Więc zespołu RxJava były przeniesieniem istniejącej klasy. Ten post powinien być wyzwaniem oryginalną konstrukcję obserwowalnych klasie przez Microsoft zamiast RxJava w płynnej klasie.]
RxJava ma niewiele ponad 3 lata, więc nie jest to przykład błędnego zaprojektowania kodu z powodu braku wiedzy na temat dobrych ( SOLID ) zasad projektowania klas (tak jak miało to miejsce we wczesnych wersjach Javy).
Dla klasy tak dużej jak Flowble jej konstrukcja wydaje się z natury niewłaściwa, ale może nie; jedna odpowiedź na to pytanie SE Jakie jest ograniczenie liczby metod klasowych? zasugerował, że odpowiedź brzmi „ Stosuj tyle metod, ile potrzebujesz ”.
Oczywiście są pewne klasy, które zgodnie z prawem potrzebują sporej liczby metod do ich obsługi bez względu na język, ponieważ nie rozpadają się one na nic mniejszego i mają spore liczby cech i atrybutów. Na przykład: ciągi, kolory, komórki arkusza kalkulacyjnego, zestawy wyników bazy danych i żądania HTTP. Posiadanie może kilkudziesięciu metod reprezentowania tych klas przez klasy nie wydaje się nierozsądne.
Ale czy Flowable naprawdę potrzebuje 460 metod, czy też jest tak ogromny, że jest koniecznie przykładem złej klasy projektu?
[Żeby było jasne: to pytanie dotyczy w szczególności RxJava w płynnej klasy zamiast obiektów Boga w ogóle.]
źródło
Odpowiedzi:
TL; DL
Brak funkcji językowych Java w porównaniu do C #, a także względy wykrywalności sprawiły, że umieściliśmy operatory źródłowe i pośrednie w dużych klasach.
Projekt
Oryginalny Rx.NET został opracowany w C # 3.0, który ma dwie kluczowe cechy: metody rozszerzenia i klasy częściowe. Ten pierwszy pozwala zdefiniować metody instancji dla innych typów, które wydają się być częścią tego typu docelowego, podczas gdy klasy częściowe pozwalają na podział dużych klas na wiele plików.
Żadna z tych funkcji nie była ani nie jest obecna w Javie, dlatego musieliśmy znaleźć sposób, aby RxJava była wystarczająco wygodna w użyciu.
Istnieją dwa rodzaje operatorów w RxJava: podobne do źródła reprezentowane przez statyczne metody fabryczne i podobne do pośrednich reprezentowane przez metody instancji. Te pierwsze mogłyby żyć w dowolnej klasie, a zatem mogły być rozdzielone na wiele klas użyteczności. Ten ostatni wymaga opracowania instancji. Koncepcyjnie, wszystko to można wyrazić metodami statycznymi, stosując jako pierwszy parametr upstream.
W praktyce jednak posiadanie wielu klas wejściowych sprawia, że odkrywanie funkcji przez nowych użytkowników jest niewygodne (pamiętaj, że RxJava musiał wprowadzić nową koncepcję i paradygmat programowania do Java), a także korzystanie z tych operatorów pośrednich w pewnym sensie koszmar. Dlatego oryginalny zespół opracował tak zwany płynny projekt API: jedna klasa, która przechowuje wszystkie metody statyczne i instancji i sama stanowi źródło lub etap przetwarzania.
Ze względu na pierwszorzędną naturę błędów, obsługę współbieżności i charakter funkcjonalny, można wymyślić wszelkiego rodzaju źródła i transformacje dotyczące przepływu reaktywnego. W miarę ewolucji biblioteki (i koncepcji) od czasów Rx.NET, dodawano coraz więcej standardowych operatorów, co z natury zwiększyło liczbę metod. Doprowadziło to do dwóch zwykłych skarg:
Pisanie reaktywnych operatorów jest trudnym zadaniem, którego nie opanowało wiele osób na przestrzeni lat; najbardziej typowi użytkownicy bibliotek nie mogą sami tworzyć operatorów (i często nie jest to konieczne, aby próbowali). Oznacza to, że od czasu do czasu dodajemy kolejnych operatorów do standardowego zestawu. W przeciwieństwie do tego odrzuciliśmy wiele innych operatorów ze względu na ich zbyt dużą dokładność lub po prostu wygodę, która nie jest w stanie znieść własnej wagi.
Powiedziałbym, że projekt RxJava rozwijał się organicznie, a nie tak naprawdę, zgodnie z pewnymi zasadami projektowania, takimi jak SOLID. Opiera się głównie na użyciu i wyczuciu płynnego API.
Inne relacje z Rx.NET
Dołączyłem do rozwoju RxJava pod koniec 2013 roku. O ile wiem, początkowe wczesne wersje 0.x były w dużej mierze ponowną implementacją czarnej skrzynki, w której ponownie wykorzystano nazwy i podpisy
Observable
operatorów Rx.NET , a także kilka decyzji architektonicznych. Dotyczyło to około 20% operatorów Rx.NET. Główną trudnością w tamtym czasie była rozdzielczość różnic językowych i platformowych między C # a Javą. Z wielkim wysiłkiem udało nam się wdrożyć wielu operatorów bez patrzenia na kod źródłowy Rx.NET i przenieśliśmy tych bardziej skomplikowanych.W tym sensie, aż do RxJava 0.19, nasz
Observable
był równoważny z Rx.NETIObservable
i jegoObservable
metodami rozszerzenia towarzyszącego . Pojawił się jednak tak zwany problem ciśnienia wstecznego i RxJava 0.20 zaczął odbiegać od Rx.NET na poziomie protokołu i architektury. Dostępne operatory zostały rozszerzone, wielu stało się świadomych przeciwciśnienia i wprowadziliśmy nowe typy:Single
orazCompletable
w erze 1.x, które nie mają obecnie odpowiedników w Rx.NET.Świadomość przeciwciśnienia znacznie komplikuje sytuację, a 1.x
Observable
otrzymał ją po namyśle. Przysięgaliśmy wierność kompatybilności binarnej, więc zmiana protokołu i interfejsu API była w większości niemożliwa.Wystąpił inny problem z architekturą Rx.NET: synchroniczne anulowanie nie jest możliwe, ponieważ w tym
Disposable
celu należy zwrócić, zanim operator zacznie wykonywać. Jednak źródła takie jakRange
chętne i nie wracają, dopóki nie skończą. Ten problem można rozwiązać, wstrzykującDisposable
do niegoObserver
zamiast zwracając jeden zsubscribe()
.RxJava 2.x został przeprojektowany i ponownie wdrożony od zera według tych zasad . Mamy osobny typ uwzględniający przeciwciśnienie,
Flowable
który oferuje ten sam zestaw operatorów coObservable
.Observable
nie obsługuje przeciwciśnienia i jest nieco równoważny z Rx.NETObservable
. Wewnętrznie wszystkie typy reaktywne wstrzykują swój uchwyt anulowania do swoich klientów, umożliwiając wydajne synchroniczne anulowanie.źródło
Przyznaję, że nie jestem zaznajomiony z biblioteką, ale przyjrzałem się omawianej klasie Flowable i wygląda na to, że działa ona jak swego rodzaju centrum. Innymi słowy, jest to klasa przeznaczona do sprawdzania poprawności danych wejściowych i odpowiedniej dystrybucji wywołań w projekcie.
Dlatego klasa ta nie byłaby tak naprawdę uważana za obiekt Boga, ponieważ obiekt Boga jest tym, który próbuje zrobić wszystko. To robi bardzo niewiele pod względem logiki. Jeśli chodzi o pojedynczą odpowiedzialność, można powiedzieć, że jedynym zadaniem klasy jest delegowanie pracy w całej bibliotece.
Naturalnie taka klasa wymagałaby metody dla każdego możliwego zadania wymaganego od
Flowable
klasy w tym kontekście. Widzisz ten sam typ wzorca z biblioteką jQuery w javascript, gdzie zmienna$
ma wszystkie funkcje i zmienne niezbędne do wykonywania wywołań w bibliotece, choć w przypadku jQuery kod nie jest po prostu delegowany, ale także sporo logiki jest uruchamiany w ramach.Myślę, że powinieneś być ostrożny, aby stworzyć taką klasę, ale ma ona swoje miejsce, dopóki programista pamięta, że jest to tylko hub, a zatem nie przekształca się powoli w obiekt boga.
źródło
Odpowiednik RX platformy .NET
Flowable
jest obserwowalny . Ma także wszystkie te metody, ale są one statyczne i można je stosować jako metody rozszerzenia . Głównym punktem RX jest to, że kompozycja jest napisana przy użyciu płynnego interfejsu .Ale aby Java miała płynny interfejs, muszą one być metodami instancyjnymi, ponieważ metody statyczne nie komponowałyby się ładnie i nie miały metod rozszerzających, aby metody statyczne można było komponować. W praktyce więc wszystkie te metody mogą być metodami statycznymi, o ile nie ma potrzeby płynnej składni interfejsu.
źródło