Rozumiem ideę zakresu pakietu, a czasami nawet myślałem, że tego chcę. Jednak za każdym razem, gdy zdecydowałem się na poważny zamiar wypróbowania go, odkryłem, że nie odpowiada on potrzebom, które, jak sądzę, będą służyć.
Moim głównym problemem zawsze wydaje się to, że rzeczy, które chciałbym ograniczyć zakres, nigdy nie są w tym samym pakiecie. Można je wszystkie koncepcyjnie połączyć, ale logiczny podział danych w aplikacji ma je jako osobne pakiety podrzędne większego pakietu.
Na przykład mogę mieć model Misji i chcę, aby tylko inne narzędzia Misji, takie jak moje usługi misji, korzystały z niektórych metod. Jednak kończę na Missions.models i Missions.services jako moich pakietach, więc MissionModel i MissionService nie są tym samym zakresem pakietu. Nigdy nie wydaje się, że istnieje sytuacja, w której pakiety odpowiednio zawierają rzeczy, które chciałbym mieć podwyższone uprawnienia bez uwzględnienia wielu rzeczy, których nie chcę mieć; i rzadko kiedy czuję, że przewaga zakresu pakietu w metodzie uzasadnia modyfikację architektury mojego projektu, aby umieścić wszystko w tym samym pakiecie. Często albo Aspekty, albo coś w rodzaju odwrócenia kontroli okazuje się lepszym podejściem do każdego problemu, dla którego krótko rozważałem określenie zakresu.
Jestem ciekawy, że jest to ogólnie uważane za prawdziwe we wszystkich programistach Java, lub jest po prostu fartem mojej pracy. Czy zakres pakietów jest często wykorzystywany w świecie rzeczywistym? Czy istnieje wiele przypadków, w których uważa się, że jest to dobra forma do użycia, czy też jest postrzegana głównie jako starsze zachowanie, które rzadko jest wykorzystywane w nowoczesnym rozwoju?
Nie pytam o to, dlaczego zakres prywatny pakietu jest domyślny, pytam, kiedy należy go używać niezależnie od wartości domyślnych. Większość dyskusji na temat tego, dlaczego jest ona domyślna, tak naprawdę nie wchodzi w grę, gdy zakres pakietu jest rzeczywiście przydatny, zamiast tego argumentując po prostu, dlaczego dwa pozostałe powszechnie używane zakresy nie powinny być domyślne, więc pakiet wygrywa przez proces eliminacji. Ponadto moje pytanie dotyczy obecnego stanu rozwoju. W szczególności opracowaliśmy do tego stopnia, że inne narzędzia i paradygmaty sprawiają, że zakres pakietu jest mniej przydatny niż wtedy, gdy decyzja o nadaniu mu domyślności ma sens.
java.util.String
ijava.util.Integer
.Odpowiedzi:
W całym
java.util.*
pakiecie występują przypadki, w których kod jest zapisywany z ochroną na poziomie pakietu. Na przykład ten bitjava.util.String
- konstruktor w Javie 6 :lub ta metoda getChars :
Powodem tego jest to, że projektanci kodu (i
java.util.*
można go uważać za raczej dużą bibliotekę) chcieli mieć możliwość szybszej wydajności kosztem utraty różnych bezpieczeństwa (kontrola zasięgu tablic, bezpośredni dostęp do innych pól implikuje raczej implementację niż interfejs, „niebezpieczny” dostęp do części klasy, które w innym przypadku są uznawane za niezmienne za pomocą metod publicznych).Ograniczając dostęp do tych metod i pól przez inne klasy w
java.util
pakiecie, umożliwia to ściślejsze powiązanie między nimi, ale także pozwala uniknąć ujawnienia tych szczegółów implementacji światu.Jeśli pracujesz nad aplikacją i nie musisz martwić się o innych użytkowników, ochrona na poziomie pakietu nie jest czymś, o co musisz się martwić. Oprócz różnych aspektów czystości projektu, możesz zrobić wszystko
public
i być z tym w porządku.Jeśli jednak pracujesz nad biblioteką, która ma być używana przez innych (lub chcesz uniknąć zbytniego zaplątania się klas w celu późniejszego refaktoryzacji), powinieneś zastosować najściślejszy poziom ochrony zapewniony dla twoich potrzeb. Często oznacza to
private
. Czasami jednak trzeba udostępnić trochę szczegółów implementacji innym klasom w tym samym pakiecie, aby uniknąć powtarzania się lub aby ułatwić faktyczną implementację kosztem połączenia dwóch klas i ich implementacji. Zatem ochrona na poziomie pakietu.źródło
Integer.toString()
iString.valueOf(int)
możesz udostępniać kod bez ujawniania go światu. Tejava.util
zajęcia są do pracy w porozumieniu w celu zapewnienia większej całości funkcjonalności zamiast być zajęcia indywidualne, które są całkowicie wyspy sami dla siebie - są ze sobą w realizacji funkcjonalności poprzez pakiet poziom scopingu opakowania oraz zakładowego.Czasami zwiększam widoczność prywatnych lub chronionych metod do pakowania, aby umożliwić testowi junit dostęp do niektórych szczegółów implementacji, do których dostęp nie powinien być uzyskiwany z zewnątrz.
ponieważ test junit ma taki sam pakiet jak testowany element, może uzyskać dostęp do tych szczegółów implementacji
przykład
Można dyskutować, czy jest to „dobre” użycie, czy nie, ale jest pragmatyczne
źródło
protected
przejść na zakres pakietu?protected
już zapewnia dostęp do paczki. To trochę dziwne dziwactwo, ale myślę, że nie chcieli wymagać deklaracji o złożonym zakresie, takich jakprotected packagescope void doSomething()
(co wymaga także nowego słowa kluczowego).Nie jest to szczególnie przydatne, zwłaszcza domyślnie, w świecie, w którym ludzie używają kropkowanych nazw paczek, tak jakby to były paczki. Ponieważ Java nie ma czegoś takiego jak pod-pakiet, więc xyinternals nie ma więcej dostępu do xy niż do ab Nazwy pakietów są takie same lub różne, częściowe dopasowanie nie różni się niczym od braku wspólnego.
Sądzę, że pierwotni programiści nigdy nie spodziewali się zastosowania tej konwencji i chcieli uprościć sprawę bez dodawania złożoności pakietów hierarchicznych w stylu Ada.
Java 9 w pewnym sensie rozwiązuje ten problem, ponieważ można umieścić kilka pakietów w module i wyeksportować tylko niektóre. Ale to sprawi, że zakres pakietu będzie jeszcze bardziej zbędny.
W idealnym świecie zmieniliby domyślny zakres na „zakres modułu, jeśli istnieje moduł zawierający, w przeciwnym razie zakres pakietu”. Następnie możesz użyć go do współdzielenia implementacji w module, umożliwiając refaktoryzację bez zerwania eksportowanych interfejsów. Ale może jest jakaś subtelność, dlaczego miałoby to zerwać z kompatybilnością wsteczną lub być w inny sposób złe.
źródło
Rodzaj spójności swój opisującym tak naprawdę nie jest najlepszym przykładem na to, gdzie należy użyć dostępu do opakowania. Pakiet jest przydatny do tworzenia komponentów, modułów, które można zamieniać w interfejsie.
Oto przybliżony przykład scenariusza. Załóżmy, że Twój kod został zreorganizowany, aby bardziej skupiał się na spójności funkcjonalnej i komponentowaniu. Być może 3 słoiki, takie jak:
Korzystając z poziomu pakietu w Mission5000.java, możesz nie mieć pewności, że żaden inny pakiet lub komponent, taki jak MissionManager, nie może ściśle powiązać z realizacją misji. To tylko element dostarczony przez „firmę trzecią” z „M co” faktycznie tworzy tam specjalną markową implementację Misji. Menedżer misji musi wywołać MissionBuiler.createMission (...) i użyć zdefiniowanego interfejsu.
W ten sposób, jeśli składnik implementacji Misji zostanie zastąpiony, wiemy, że implementatorzy pliku MissionManager.jar nie ominęli interfejsu API i nie używają bezpośrednio implementacji MCo.
Stąd możemy zamienić MCo-missionizer5000.jar na konkurencyjny produkt. (A może próbka do testowania jednostek przez Kierownika misji)
I odwrotnie, MCo potrafi ukryć swoje tajemnice handlowe misjonarzy.
(Jest to związane z egzekwowaniem pomysłów dotyczących niestabilności i abstrakcyjności również w komponentach.)
źródło
Ponieważ zakres pakietu w Javie nie jest zaprojektowany w sposób hierarchiczny, jest to mało użyteczne. W ogóle nie korzystamy z zakresu pakietu i definiujemy wszystkie klasy jako publiczne.
Zamiast tego używamy własnych reguł dostępu opartych na hierarchii pakietów i wstępnie zdefiniowanych nazwach pakietów.
Jeśli interesują Cię szczegóły, przejdź do Google dla „Reguł CODERU”.
źródło