So sądzicie o modyfikatorze dostępu "package-private"?

0

Co sądzicie o package private? Apkę dzieli się na moduły typu order, paymanet itd, wszystko jest package private prócz facady i dtosów, które są publiczne i przez nie komunikuje się z modułem. Wydaje się to całkiem ok tylko problem w tym, że w Javie ten package private jest skopany, bo nie ma subpackegów przez co w domenie zamiast w infrastrukturze trzeba trzymać np. Springowe repozytoria czy konfigurację beanów. Czasami jak moduł jest spory to robi się chaos.
Tutaj prezentacja na ten temat: Modularity and hexagonal architecture in real life: Jakub Nabrdalik

1
Nofenak napisał(a):

wszystko jest package private prócz facady i dtosów, które są publiczne i przez nie komunikuje się z modułem.

Gratulacje, świetny design.

Wydaje się to całkiem ok tylko problem w tym, że w Javie ten package private jest skopany, bo nie ma subpackegów przez co w domenie zamian w infrastrukturze trzeba trzymać np. Springowe repozytoria czy konfigurację beanów. Czasami jak moduł jest spory to robi się chaos.
Tutaj prezentacja na ten temat: Modularity and hexagonal architecture in real life: Jakub Nabrdalik

Java jest jaka jest, stosuj najniższy modyfikator wszędzie gdzie się da.

  • Jeśli coś może być private, zrób private
  • jeśli nie, to jeśli coś może być protected, zrób protected
  • jeśli nie, to jeśli coś może być package-private, zrób package-private
  • jeśli nie, to zrób public

Proste.

Co co samego zamysłu, to moim zdaniem również jest skopany ten modyfikator, ale nie ma nic innego.

4

package-private jest skopany, ale lepsze to niż nic. Dużo lepszą ideą jest kotlinowy internal, ale on też działa tak średniawo.

Warto używać, ale takiego podejścia w pełni jak w prezentacji nie polecam. Już widziałem i efekt jest taki, że mamy 50 pięknie zenkapsulowanych klas w jednym pakiecie i przejrzystość takiego pakietu pozostawia bardzo wiele do życzenia.

6

Enkapsulacja jest ważna ale jak masz dobrze zorganizowany projekt z np. Architektura heksagonalna, wiesz z czym sie je ta architektura, w zespole przez CR nie przechodzi byle gunwo to można żyć z package private.

Z reguły jak rozszerzam obecne funkcjonalności to wiem gdzie jakie klasy powinny być lub z jakich klas powinienem skorzystać. Jest to taka świadomość level wyżej niż podejście "skorzystam z tego co jest public".

Więcej jest myślenia pod kątem architektury aniżeli tego co się da bo jest publiczne aczkolwiek działa to razem z enkapsulacja tego co faktycznie się da.

3

@Nofenak

Javie ten package private jest skopany, bo nie ma subpackegów

Nie jest skopany. Pakiety w Javie działają jako osobne przestrzenie nazw, które nie są w żaden sposób ze sobą połączone. W dodatku domyślny modyfikator działa inaczej w przypadku interfejsów i inaczej w przypadku klas.

domenie zamiast w infrastrukturze trzeba trzymać np. Springowe repozytoria czy konfigurację beanów

Konfiguracja może być package-private i wtedy masz konfigurację per pakiet.

W Javie interfejsy są pomyślane jako coś co definiuje kontrakt. Dlatego domyślny modyfikator metod w interfejsach działa jak public w klasach. Klasy znowuż mają być domyślnie ukryte, dlatego domyślny modyfikator ogranicza widoczność do bierzącego pakietu. I to jest całkiem dobry pomysł, choć brakowało mu jednej rzeczy – nie dało się ograniczyć interfejsu tak, by zachowywał się jak klasa final. Obecnie mamy już sealed i nie jest to problem. Podobnie z klasami, mamy rekordy więc odpadł problem publicznych klas DTO.

@Grzyboo:

Już widziałem i efekt jest taki, że mamy 50 pięknie zenkapsulowanych klas w jednym pakiecie i przejrzystość takiego pakietu pozostawia bardzo wiele do życzenia.

Jest coś takiego jak spójność i zależność klasy. Te metryki mówią odpowiednio

  • spójność – jak elementy w ramach klasy zależą od siebie,
  • zależność – jak klasa zależy od innych klas.

Wysoka spójność oznacza, że kod jest łatwy w utrzymaniu i rozwoju. Każdy element ma sens i jest powiązany z innymi.
Niska zależność oznacza, że kod nie korzysta zbytnio z zewnętrzych odwołań czy modułów.

To samo możesz przenieść na poziom pakietów. Jeżeli twój pakiet ma 50+ klas to oznacza, że coś jest nie tak. Czy te klasy współpracują ze sobą w jakiś sposób, czy pakiet jest spójny? Czy po prostu zrobiłeś wór na wszystko, co pasowało do nazwy pakietu?

0

Mały offtopic.

Koziołek napisał(a):

W Javie interfejsy są pomyślane jako coś co definiuje kontrakt. Dlatego domyślny modyfikator metod w interfejsach działa jak public w klasach. Klasy znowuż mają być domyślnie ukryte, dlatego domyślny modyfikator ogranicza widoczność do bierzącego pakietu.

Zgadzam się z całą wypowiedzią, ale to "W Javie interfejsy są pomyślane jako coś co definiuje kontrakt." brałbym z przymróżeniem oka. Rozumiem, że napisałes to w kontekście wyjaśnienia czemu brak modyfikatora w interfejsie domyślnie znaczy public, w odróżnieniu od klas.

Ale to z tym, że interfejs to kontrakt, to chyba za daleko idąca teza. No bo skoro tak, to klasa abstrakcyjna też mogłaby definiować kontrakt (np klasa abstrakcyjna mająca same abstrakcyjne metody i 0 logiki, jest tożsama z interfejsem z programowego punktu widzienia, poza tym że nie ma wielodziedziczenia w javie, ale to jednak wada języka a nie klas abstrakcyjnych), a jednak w niej pusty modyfikator to też jest package-private.

0
Riddle napisał(a):

Ale to z tym, że interfejs to kontrakt, to chyba za daleko idąca teza. No bo skoro tak, to klasa abstrakcyjna też mogłaby definiować kontrakt, a jednak w niej pusty modyfikator to też jest package-private.

Jak chcesz inaczej to zrób interfejs z metodami default i pozamiatane.

0
_13th_Dragon napisał(a):
Riddle napisał(a):

Ale to z tym, że interfejs to kontrakt, to chyba za daleko idąca teza. No bo skoro tak, to klasa abstrakcyjna też mogłaby definiować kontrakt, a jednak w niej pusty modyfikator to też jest package-private.

Jak chcesz inaczej to zrób interfejs z metodami default i pozamiatane.

Chodzi o to ze pojęcie "interfejs to kontrakt" jest niespójne w kontekście języka.

1
Riddle napisał(a):

Chodzi o to ze pojęcie "interfejs to kontrakt" jest niespójne w kontekście języka.

Od java8 już jest. Klasy abstrakcyjne, to raczej kontrakt na poziomie implementacji (dalej idąc na poziomie hierarchi), a nie komunikacji pomiędzy klasami.

0
Koziołek napisał(a):
Riddle napisał(a):

Chodzi o to ze pojęcie "interfejs to kontrakt" jest niespójne w kontekście języka.

Od java8 już jest. Klasy abstrakcyjne, to raczej kontrakt na poziomie implementacji (dalej idąc na poziomie hierarchi), a nie komunikacji pomiędzy klasami.

Nie zgadzam się, bo:

  • interfejs z domyślnymi metoda vs. klasa abstrakcyjna
  • oraz, interfejs bez domyślnych method vs klasa abstrakcyjna tylko z abstrakcyjnymi metodami

to jest praktycznie jedno i to samo.

Jestem zdania, że interfejs to jest hack w języku, zastosować wielodziedziczenie method abstrakcyjnych (jeszcze w starych javach). Skoro da się wielo dziedziczyć interfejsy z default metodami, to powinno dać się móc również dziedziczyć klasy abstrakcyjne.

Zastanówmy się, poza "intencją", to czym się różnią interfejsy od klas abstrakcyjnych, po co jest w języku jedno i drugie? Oba mogą mieć metody abstrakcyjne, oba pozwalają na polimorfizm, oba mogą mieć metody które mają implementacje i mogą być nadpisane. Różnią się tylko tym, jakim keywordem się je deklaruje i extenduje. Śmietnik, if you ask me.

0

@Riddle: jest jeszcze kwestia dziedziczenia i własności.

1
RequiredNickname napisał(a):

Enkapsulacja jest ważna ale jak masz dobrze zorganizowany projekt z np. Architektura heksagonalna, wiesz z czym sie je ta architektura, w zespole przez CR nie przechodzi byle gunwo to można żyć z package private.

Podoba mi się to podejście w stylu "da się z tym skopanym package-private żyć" :)

Ogólnie - nie uważam, żeby package-private był skopany - bo on robi, co do niego należy. Uważam, że po prostu w Javie brakuje modyfikatora, który ograniczałby widoczność poza podpakiety (tj. klasa z pakietu project.mypackage powinna być widoczna w myproject.mypackage.sub, ale już nie w myproject).
Drugą opcją jest zejście w dół drzewka, tj. klasa z projektu myproject.mypackage.sub powinna być widoczna w pakiecie myproject, myproject.mypackage, ale już nie w np. myproject.otherpackage lub myproject.mypackage.othersub.

Takie dwa modyfikatory (a nawet i jeden z nich) załatwiłyby 99% problemów z kombinowaniem z widocznością klas. I nawet nie musiałoby to być w runtime, ale jedynie w momencie kompilacji.

Riddle napisał(a):

Zastanówmy się, poza "intencją", to czym się różnią interfejsy od klas abstrakcyjnych, po co jest w języku jedno i drugie? Oba mogą mieć metody abstrakcyjne, oba pozwalają na polimorfizm, oba mogą mieć metody które mają implementacje i mogą być nadpisane. Różnią się tylko tym, jakim keywordem się je deklaruje i extenduje. Śmietnik, if you ask me.

Nie wiem, czy śmietnik czy nie, ale klasy abstrakcyjne różnią się od interfejsów w Javie, i to mocno.

  1. Klasy abstrakcyjne mogą mieć konstruktory, a każda klasa dziedzicząca musi jeden z konstruktorów klasy abstrakcyjnej wywołać.
  2. Klasy abstrakcyjne mogą mieć zdefiniowane pola prywatne.
  3. W interfejsach (tj. w domyślnych interpretacjach) nie istnieje słówko kluczowe super, czyli takie coś ci się nie skompiluje:
interface MyInterface {
    default void sayHello() {
        System.out.println("Hello");
    }
}

interface MyInterface2 extends MyInterface {
    default void sayHello() {
        super.sayHello(); // tutaj poleci błąd kompilacji
        System.out.println("Hello2");
    }
}

W rezultacie jeśli chcesz zachować LSP to w tych metodach domyślnych to musisz kopiować zachowanie. W przypadku klas abstrakcyjnych możesz użyć po prostu super.sayHello().

Ogólnie nie chcę się kłócić, czy jest to dobra czy zła idea, ale twierdzenie, że Różnią się tylko tym, jakim keywordem się je deklaruje i extenduje to po prostu nieprawda.

0
wartek01 napisał(a):

Ogólnie - nie uważam, żeby package-private był skopany - bo on robi, co do niego należy. Uważam, że po prostu w Javie brakuje modyfikatora, który ograniczałby widoczność poza podpakiety (tj. klasa z pakietu project.mypackage powinna być widoczna w myproject.mypackage.sub, ale już nie w myproject).

Ale w javie nie ma czegoś takiego jak podpakiet. Pakiet a.b.c nie ma nic wspólnego z pakietem a.b.

0
wartek01 napisał(a):

Ogólnie nie chcę się kłócić, czy jest to dobra czy zła idea, ale twierdzenie, że Różnią się tylko tym, jakim keywordem się je deklaruje i extenduje to po prostu nieprawda.

Okej, pozwól że sprostuję.

Klasy abstrakcyjne od interfejsów różnią się tylko tym:

  • jakim keywordem się je deklaruje i extenduje
  • klasy abstrakcyjne mogą mieć dodatkowo konstruktor i pola prywatne
  • klasy abstrakcyjne mogą zawołac super

Tak czy tak, to nie wyjaśnia po co w Javie są dwa byty po to. interface to zaszłość, kiedy problem diamentu był na tyle istotny żeby wpłynąć na język, a teraz jest właściwie niepotrzebny i istnieje przez backwards-compatibility. Nie ma nic co mogą zrobić interfejsy, czego nie mogą klasy abstrakcyjne (oprócz wielodziedziczenia, ale skoro interfejsy teraz mogą wielodziedziczyć z default metodami, to klasy abstrakcyjne też by mogły).

0
Riddle napisał(a):

Ale w javie nie ma czegoś takiego jak podpakiet. Pakiet a.b.c nie ma nic wspólnego z pakietem a.b.

Słówko "subpackage" jest jak najbardziej w użyciu: https://docs.oracle.com/javase/specs/jls/se13/html/jls-7.html

Tak czy tak, to nie wyjaśnia po co w Javie są dwa byty po to.

Masz rację w tym, że prawie wszystko (oprócz wielodziedziczenia) co oferują interfejsy dałoby się rozwiązać klasami abstrakcyjnymi. Pozostaje pytanie - czy trzeba?
Ograniczenia pewnych rzeczy na poziomie językowym potrafią być pożyteczne. Obecnie w Javie interfejsy definiują zachowania, a dziedziczenie klas relację "is a" (a przynajmniej taką relację oznaczać powinno).
Trudno mi powiedzieć, czy np. twórcy Scali czy Kotlina myślą w ten sam sposób, ale wiem, że świadomie zaimplementowali model dziedziczenia z podziałem na klasy i interfejsy tak samo.

0
wartek01 napisał(a):
Riddle napisał(a):

Ale w javie nie ma czegoś takiego jak podpakiet. Pakiet a.b.c nie ma nic wspólnego z pakietem a.b.

Słówko "subpackage" jest jak najbardziej w użyciu: https://docs.oracle.com/javase/specs/jls/se13/html/jls-7.html

W dokumentacjach często wymyśla się stwierdzenia na potrzeby wyjasnienia czegoś, ale takie elementy nie koniecznie muszą mieć przełożenie na element języka.

W javie package a.b.c ma relację z pakietem a.b dokładnie taką samą jak z d.e. Niektórzy ludzie mylą strukturę folderów w systemie plików z pakietami, i z tego wynika ten błąd. Jeśli plik jest w folderze /a/b/c/file.txt, to znaczy że jest też w /a/b/, ale pakiety to nie foldery. I to że jakaś klasa jest widoczna w a.b.c nie znaczy że jest widoczna w a.b.

Owszem, struktura nazw pakietów, musi odpowiadać strukturze nazw folderów w Javie; ale to nie znaczy że pakiet jest folderem, albo że kryteria prawdziwe dla folderów muszą być tak samo prawdziwe dla pakietów.

wartek01 napisał(a):

Masz rację w tym, że prawie wszystko (oprócz wielodziedziczenia) co oferują interfejsy dałoby się rozwiązać klasami abstrakcyjnymi. Pozostaje pytanie - czy trzeba?
Ograniczenia pewnych rzeczy na poziomie językowym potrafią być pożyteczne. Obecnie w Javie interfejsy definiują zachowania, a dziedziczenie klas relację "is a" (a przynajmniej taką relację oznaczać powinno).

Dziedziczenie to nie jest relacja "is a". Nie ma w ogóle czegoś takiego jak relacja "is a", to jakieś romantyczne wyobrażenie. Dziedziczenie to nic innego jak redeklaracja memeberów w podklasie. Takie przekonanie to jest moim zdaniem jeden z powodów, czemu mamy dwa praktycznie tożsame byty ze sobą w języku.

Pod tym względem doceniam Zenbook of Python, w którym jedna z zasad mówi: "There should be one-- and preferably only one --obvious way to do it.". Klasy abstrakcyjne i interfejsy łamią tę zasadę.

wartek01 napisał(a):

Trudno mi powiedzieć, czy np. twórcy Scali czy Kotlina myślą w ten sam sposób, ale wiem, że świadomie zaimplementowali model dziedziczenia z podziałem na klasy i interfejsy tak samo.

No ja się z taką decyzją nie zgadzam, ale mogę się domyślać że po prostu skopiowali zaszłość.

Mogli też mieć nie-programistyczne powody, np niektórzy programiści uważają interfejsy za "czyste", a klasy abstrakcyjne za "potencjalnie brudne".

0
Riddle napisał(a):

W javie package a.b.c ma relację z pakietem a.b dokładnie taką samą jak z d.e.

Tak z ciekawości - po co to piszesz? Przecież ja nigdzie nie napisałem, że tak jest.
Napisałem, czego mi brakuje, a w odpowiedzi od ciebie mogłem przeczytać, że w Javie nie ma czegoś takiego jak "podpakiet". Kiedy stwierdziłem, że jest to słowo używane w dokumentacji (więc mogę i ja go sobie używać w opisie tego, czego mi brakuje) to zacząłeś mi tłumaczyć, że w Javie coś nie działa tak, jak ja napisałem, że nie działa.

No ja się z taką decyzją nie zgadzam, ale mogę się domyślać że po prostu skopiowali zaszłość.

Mogli też mieć nie-programistyczne powody, np niektórzy programiści uważają interfejsy za "czyste", a klasy abstrakcyjne za "potencjalnie brudne".

Widzisz, jest bardzo duża różnica pomiędzy "ja się z taką decyzją nie zgadzam" i "ta decyzja nie ma sensu".
Istnieje sporo języków nie-JVMowych (np. TypeScript), które posiadają rozdział na interfejsy i klasy abstrakcyjne. Więc może według części twórców języków istnieje jakiś sens rozdziału ciężkiej abstrakcji od lekkiej, i wynika on po prostu z przyjętych konwencji, przez co kod części osób pisze i czyta się łatwiej.

0
wartek01 napisał(a):
Riddle napisał(a):

W javie package a.b.c ma relację z pakietem a.b dokładnie taką samą jak z d.e.

Tak z ciekawości - po co to piszesz? Przecież ja nigdzie nie napisałem, że tak jest.
Napisałem, czego mi brakuje, a w odpowiedzi od ciebie mogłem przeczytać, że w Javie nie ma czegoś takiego jak "podpakiet". Kiedy stwierdziłem, że jest to słowo używane w dokumentacji (więc mogę i ja go sobie używać w opisie tego, czego mi brakuje) to zacząłeś mi tłumaczyć, że w Javie coś nie działa tak, jak ja napisałem, że nie działa.

No ja się z taką decyzją nie zgadzam, ale mogę się domyślać że po prostu skopiowali zaszłość.

Mogli też mieć nie-programistyczne powody, np niektórzy programiści uważają interfejsy za "czyste", a klasy abstrakcyjne za "potencjalnie brudne".

Widzisz, jest bardzo duża różnica pomiędzy "ja się z taką decyzją nie zgadzam" i "ta decyzja nie ma sensu".
Istnieje sporo języków nie-JVMowych (np. TypeScript), które posiadają rozdział na interfejsy i klasy abstrakcyjne. Więc może według części twórców języków istnieje jakiś sens rozdziału ciężkiej abstrakcji od lekkiej, i wynika on po prostu z przyjętych konwencji, przez co kod części osób pisze i czyta się łatwiej.

No i widzisz, tylko jak porównujesz do tego TypeScript'a, to tam istnienie interfejsu ma "jakiś sens", to znaczy tam decyzja wzięła się stąd że klasa TypeScript jest transpilowana na JS; a interface nie ma emita w TS. Więc to ma "jakieś" (moim zdaniem nadal złe, ale jakieś) wytłumaczenie (choćby czysto techniczne).

W Javie jednak nie ma takiego wytłumaczenia - zła decyzja podjęta z uwagi na trudności stworzenia takiego rozwiązania. Jakbyś prześledził historię Javy, to wiedziałbyś że interfejs został dodany, tylko i wyłącznie po to, żeby ominąć problem diamentu w klasach abstrakcyjnych. Teraz problem diamentu jest rozwiązany, więc interfejs nie ma racji bytu - jedyną racją jest to że ludzie się do niego przywiązali; a z javy nie został usunięty przez backwards compatibility.

0

Używanie modyfikatorów dostępu to jak kupowanie alkoholu, a później zamykanie go w szafce na klucz, a później oddawanie znajomemu tego klucza, żeby nie kusiło. Jeżeli nie potrafisz się kontrolować i musisz używać prywatnych pól, które autor specjalnie oznaczył prefiksem _, to może nie powinieneś siadać za klawiaturą.

Niedawno tłumaczyłem jednej lead dżawowczyni, że jak widzi metody prefiksowane _ w Pythonie to ma tego nie dotykać, a ta jeszcze krytykuje to podejście bo przecież można się do tego dobrać. Napisane jest, żeby nie dotykać to masz nie dotykać. Masz 5 lat, że tego nie rozumiesz?

Jak jedziesz autobusem to rozumiem, że też nie płacisz za bilet bo przecież da się wsiąść bez biletu. Brawo, oszukałeś system. Tylko kogo tak naprawdę oszukałeś bo chyba siebie.

0
Riddle napisał(a):

W Javie jednak nie ma takiego wytłumaczenia - zła decyzja podjęta z uwagi na trudności stworzenia takiego rozwiązania. Jakbyś prześledził historię Javy, to wiedziałbyś że interfejs został dodany, tylko i wyłącznie po to, żeby ominąć problem diamentu w klasach abstrakcyjnych. Teraz problem diamentu jest rozwiązany, więc interfejs nie ma racji bytu - jedyną racją jest to że ludzie się do niego przywiązali; a z javy nie został usunięty przez backwards compatibility.

Czyli jedyną racją bytu podziału na interfejsy i klasy abstrakcyjne jest to, że istnieją ludzie, którzy uznają podział na lekką i ciężką abstrakcję za przydatny. Zaś twoim argumentem przeciw temu podziałowi jest to, że ty personalnie uznajesz go za bezsensowny.
No tak, pełna zgoda.

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