Zadaję to pytanie, ponieważ uważam, że zrobili to z bardzo dobrego powodu i że większość ludzi nie używa go właściwie, dobrze z mojego dotychczasowego doświadczenia w branży. Ale jeśli moja teoria jest prawdziwa, to nie jestem pewien, dlaczego zawarli modyfikator dostępu prywatnego ...?
Uważam, że prawidłowe użycie domyślnego dostępu zapewnia większą testowalność przy zachowaniu enkapsulacji. I sprawia, że modyfikator dostępu prywatnego jest zbędny.
Domyślnego modyfikatora dostępu można użyć, aby zapewnić ten sam efekt, używając unikalnego pakietu dla metod, które muszą być ukryte przed resztą świata, i robi to bez uszczerbku dla testowalności, ponieważ pakiety w folderze testowym z tym samym są w stanie uzyskać dostęp do wszystkich domyślnych metod zadeklarowanych w folderze źródłowym.
Uważam, że właśnie dlatego Java używa dostępu do pakietu jako „domyślnego”. Ale nie jestem pewien, dlaczego obejmowały one również dostęp prywatny, jestem pewien, że istnieje ważny przypadek użycia ...
Odpowiedzi:
Myślę, że mieli dobry pomysł na to, co zrobiłby przeciętny programista. Przez przeciętnego programistę mam na myśli kogoś, kto nie jest naprawdę dobry w programowaniu, ale i tak dostaje pracę, ponieważ nie ma wystarczającej liczby dobrych i są one drogie.
Gdyby „upublicznili” dostęp do domyślnego, większość programistów nigdy nie zawracałaby sobie głowy korzystaniem z czegokolwiek innego. Rezultat byłby bardzo duży wszędzie kodu spaghetti . (Bo jeśli technicznie możesz dzwonić z dowolnego miejsca, po co zawracać sobie głowę logiką w ramach metody?)
Ustawienie domyślnego dostępu jako „prywatnego” byłoby tylko nieco lepsze. Większość początkujących programistów po prostu wymyśla (i dzieli się na swoich blogach) ogólną zasadę, że „musisz pisać wszędzie„ publicznie ”, a następnie narzekają, dlaczego Java jest tak zła, że zmusza ich do pisania wszędzie„ publicznie ”. I produkują też dużo kodu spaghetti.
Dostęp do pakietu jest czymś, co pozwala programiście korzystać ze swoich niechlujnych technik programowania podczas pisania kodu w jednym pakiecie, ale następnie muszą ponownie rozważyć to przy tworzeniu większej liczby pakietów. Jest to kompromis między dobrymi praktykami biznesowymi a brzydką rzeczywistością. Na przykład: napisz kod spaghetti, jeśli nalegasz, ale zostaw brzydki bałagan w paczce; przynajmniej stworzyć lepsze interfejsy między pakietami.
Prawdopodobnie są też inne powody, ale nie doceniłbym tego.
źródło
Dostęp domyślny nie powoduje, że modyfikator dostępu prywatnego jest zbędny.
Stanowisko projektantów języka znajduje odzwierciedlenie w oficjalnym samouczku - Kontrolowanie dostępu do członków klasy i jest dość jasne (dla Twojej wygody, odpowiednie oświadczenie w cytacie pogrubione ):
Twoje odwołanie do testowalności jako uzasadnienia całkowitego usunięcia prywatnego modyfikatora jest błędne, o czym świadczą np. Odpowiedzi w New to TDD. Czy powinienem teraz unikać prywatnych metod?
Stanowisko projektantów języków na temat celu i korzystania z dostępu na poziomie pakietu zostało wyjaśnione w innym oficjalnym samouczku, Tworzenie i korzystanie z pakietów i nie ma nic wspólnego z pomysłem porzucenia prywatnych modyfikatorów (dla Twojej wygody odpowiednie oświadczenie w pogrubionej wycenie ) :
<rant "Myślę, że słyszałem dość marudzenia. Chyba czas powiedzieć głośno i wyraźnie ...">
Metody prywatne są korzystne dla testów jednostkowych.
Uwaga poniżej zakłada, że znasz zakres kodu . Jeśli nie, poświęć trochę czasu na naukę, ponieważ jest to bardzo przydatne dla osób zainteresowanych testowaniem jednostkowym i testowaniem w ogóle.
W porządku, więc mam tę prywatną metodę i testy jednostkowe, a analiza zasięgu mówi mi, że jest luka, moja prywatna metoda nie jest objęta testami. Teraz...
Co zyskuję, zachowując prywatność
Ponieważ metoda jest prywatna, jedynym sposobem na kontynuację jest przestudiowanie kodu, aby dowiedzieć się, jak jest on używany przez nieprywatny interfejs API. Zazwyczaj takie badanie ujawnia, że przyczyną luki jest brak konkretnego scenariusza użycia w testach.
Ze względu na kompletność, innymi (rzadziej) przyczynami takich luk w zasięgu mogą być błędy w specyfikacji / projekcie. Nie zagłębię się tutaj głęboko, aby wszystko było proste; wystarczy powiedzieć, że jeśli osłabisz ograniczenie dostępu „tylko po to, aby metoda była testowalna”, stracisz szansę dowiedzenia się, że te błędy w ogóle istnieją.
W porządku, aby naprawić lukę, dodaję test jednostkowy na brakujący scenariusz, powtarzam analizę pokrycia i sprawdzam, czy luka zniknęła. Co mam teraz Mam jako nowy test jednostkowy do konkretnego użycia nieprywatnego API.
Nowy test gwarantuje, że oczekiwane zachowanie dla tego użycia nie zmieni się bez powiadomienia, ponieważ jeśli się zmieni, test się nie powiedzie.
Zewnętrzny czytelnik może przyjrzeć się temu testowi i dowiedzieć się, jak powinien on używać i zachowywać się (tutaj, zewnętrzny czytelnik obejmuje moje przyszłe ja, ponieważ zwykle zapominam o kodzie miesiąc lub dwa po tym, jak skończę z nim).
Nowy test jest odporny na refaktoryzację (czy refaktoryzuję metody prywatne? Założycie się!) Cokolwiek zrobię
privateMethod
, zawsze będę chciał przetestowaćnonPrivateMethod(true)
. Bez względu na to, co zrobięprivateMethod
, nie będzie potrzeby modyfikowania testu, ponieważ metoda nie jest wywoływana bezpośrednio.Nie jest zły? Stawiasz zakład
Co tracę z osłabienia ograniczenia dostępu
Teraz wyobraź sobie, że zamiast wyżej, po prostu osłabiam ograniczenie dostępu. Pomijam przestudiowanie kodu używającego tej metody i przechodzę od razu do testu, który mnie wywołuje
exPrivateMethod
. Świetny? Nie!Czy dostaję test na określone użycie nieprywatnego API wymienionego powyżej? Nie: wcześniej nie było testu
nonPrivateMethod(true)
i teraz nie ma takiego testu.Czy czytelnicy z zewnątrz mają szansę lepiej zrozumieć wykorzystanie klasy? Nie. "- Hej, jaki jest cel testowanej tutaj metody? - Zapomnij, to jest wyłącznie do użytku wewnętrznego. - Ups."
Czy toleruje refaktoryzację? Nie ma mowy: cokolwiek się zmieni
exPrivateMethod
, prawdopodobnie przerwie test. Zmień nazwę, połącz się z inną metodą, zmień argumenty i test po prostu przestanie się kompilować. Bół głowy? Obstawiasz!Podsumowując , trzymanie się metody prywatnej przynosi mi użyteczne, niezawodne ulepszenie w testach jednostkowych. W przeciwieństwie do tego, osłabienie ograniczeń dostępu „do testowalności” daje mi tylko niejasny, trudny do zrozumienia fragment kodu testowego, który dodatkowo jest narażony na stałe ryzyko złamania przez jakiekolwiek drobne refaktoryzacja; szczerze mówiąc, to, co dostaję, wygląda podejrzanie jak dług techniczny .
</rant>
źródło
Najbardziej prawdopodobnym powodem jest to, że ma to związek z historią. Przodek Javy, Oak, miał tylko trzy poziomy dostępu: prywatny, chroniony, publiczny.
Tyle że private w Oak było odpowiednikiem pakietu private w Javie. Możesz przeczytać rozdział 4.10 specyfikacji językowej Oaka (moje podkreślenie):
Tak więc, twoim zdaniem, prywatny dostęp, znany w Javie, początkowo nie istniał. Ale kiedy masz więcej niż kilka klas w pakiecie, nie posiadanie prywatnego, jak wiemy, doprowadziłoby to do koszmaru kolizji nazw (na przykład java.until.concurrent ma prawie 60 klas), prawdopodobnie dlatego przedstawił to.
Jednak semantyka domyślnego dostępu do pakietu (pierwotnie zwana prywatnym) nie została zmieniona między Oak i Java.
źródło
Są sytuacje, w których chcielibyśmy mieć dwie oddzielne klasy, które są ściślej związane niż ujawnianie wszystkiego publicznie. Ma to podobne pomysły na „przyjaciół” w C ++, gdzie inna klasa, która jest zadeklarowana jako „przyjaciel” innej osoby, może uzyskać dostęp do swoich prywatnych członków.
Jeśli zajrzysz do wnętrza klas takich jak BigInteger , znajdziesz szereg domyślnej ochrony pakietów dla pól i metod (wszystko, co jest niebieskim trójkątem na liście). Pozwala to innym klasom w pakiecie java.math na bardziej optymalny dostęp do ich wewnętrznych funkcji dla określonych operacji (można znaleźć te metody nazywane w BigDecimal - BigDecimal jest wspierany przez BigInteger i zapisuje ponowne wdrożenie BigInteger ponownie . To, że są one na poziomie pakietu, nie jest ' Problem ochrony, ponieważ java.math jest zapieczętowanym pakietem i nie można do niego dodawać żadnych innych klas.
Z drugiej strony istnieje wiele rzeczy, które są rzeczywiście prywatne, tak jak powinny. Większość paczek nie jest zamknięta. Bez prywatnego twój współpracownik mógłby umieścić inną klasę w tym pakiecie i uzyskać dostęp do (już nie) prywatnego pola i przerwać enkapsulację.
Warto zauważyć, że testowanie jednostkowe nie było czymś, o czym myślano wstecz, gdy budowano lunety ochronne. JUnit (środowisko testowe XUnit dla Javy) powstało dopiero w 1997 r. (O jednym opowiadaniu można przeczytać na stronie http://www.martinfowler.com/bliki/Xunit.html ). Wersje JDK w wersji Alpha i Beta były w 1995 r., A JDK 1.0 w 1996 r., Ale poziomy ochrony tak naprawdę nie zostały obniżone aż do JDK 1.0.2 (wcześniej można było mieć
private protected
poziom ochrony).Niektórzy twierdzą, że domyślnie nie powinien to być poziom pakietu, ale prywatny. Inni twierdzą, że nie powinna istnieć domyślna ochrona, ale wszystko powinno być wyraźnie określone - niektórzy z tych osób będą pisać kod, taki jak:
Zanotuj tam komentarz.
Prawdziwy powód, dla którego ochrona na poziomie pakietu jest domyślna, prawdopodobnie został utracony w notatkach projektowych Java (kopałem i nie mogę znaleźć żadnego powodu, dla którego artykuły, wiele osób wyjaśnia różnicę, ale nikt nie mówi „to jest powód „).
Więc zgadnę. I to wszystko - zgadnij.
Po pierwsze, ludzie wciąż próbowali rozgryźć język. Od tego czasu języki uczyły się na błędach i sukcesach Javy. Może to być wymienione jako błąd polegający na braku czegoś, co należy zdefiniować dla wszystkich pól.
public
, aprivate
ponieważ mają one największy wpływ na dostęp.Tak więc prywatność nie jest domyślna i dobrze, wszystko inne ułożyło się na swoim miejscu. Domyślny zakres pozostawiono jako domyślny. Być może był to pierwszy zakres, który został stworzony i w celu zachowania ścisłej kompatybilności wstecznej ze starszym kodem, tak zostało.
Jak już powiedziałem, są to zgadywanki .
źródło
Enkapsulacja to sposób na powiedzenie „nie musisz o tym myśleć”.
Ujęcie ich w nietechnicznych terminach.
źródło
Nie sądzę, żeby skupili się na testowaniu, po prostu dlatego, że testowanie nie było wtedy zbyt powszechne.
Myślę, że chcieli osiągnąć enkapsulację na poziomie pakietu. Klasa może, jak wiesz, mieć wewnętrzne metody i pola i ujawniać tylko niektóre z nich przez członków publicznych. W ten sam sposób pakiet może mieć wewnętrzne klasy, metody i pola i ujawniać tylko niektóre z nich. Jeśli tak myślisz, pakiet ma wewnętrzną implementację w zestawie klas, metod i pól, wraz z interfejsem publicznym w innym (być może częściowo pokrywającym się) zestawie klas, metod i pól.
Najbardziej doświadczeni koderzy, których znam, myślą w ten sposób. Biorą enkapsulację i stosują ją na poziomie abstrakcji wyższym niż klasa: pakiet lub komponent, jeśli chcesz. Wydaje mi się rozsądne, że projektanci Javy byli już tak dojrzali w swoich projektach, biorąc pod uwagę, jak dobrze Java wciąż wytrzymuje.
źródło