szukasz konkretnego przypadku użycia, w którym zarówno podklasa, jak i klasa w tym samym pakiecie muszą uzyskać dostęp do chronionego pola lub metody ...
Cóż, dla mnie taki przypadek użycia jest raczej ogólny niż konkretny i wynika z moich preferencji:
- Zacznij od możliwie jak najściślejszego modyfikatora dostępu, uciekając się do słabszego (-ych) dopiero później, gdy uzna to za konieczne.
- Testy jednostkowe powinny znajdować się w tym samym pakiecie co testowany kod.
Z góry mogę rozpocząć projektowanie dla moich obiektów z domyślnymi modyfikatorami dostępu (zacznę od, private
ale skomplikowałoby to testowanie jednostek):
public class Example {
public static void main(String [] args) {
new UnitTest().testDoSomething(new Unit1(), new Unit2());
}
static class Unit1 {
void doSomething() {} // default access
}
static class Unit2 {
void doSomething() {} // default access
}
static class UnitTest {
void testDoSomething(Unit1 unit1, Unit2 unit2) {
unit1.doSomething();
unit2.doSomething();
}
}
}
Dygresja w powyższym fragmencie, Unit1
, Unit2
i UnitTest
są zagnieżdżone wewnątrz Example
dla uproszczenia prezentacji, ale w rzeczywistym projekcie, będę prawdopodobnie mieć tych klas w osobnych plikach (a UnitTest
nawet w osobnym katalogu ).
Następnie, gdy zajdzie taka potrzeba, osłabiłbym kontrolę dostępu z domyślnych do protected
:
public class ExampleEvolved {
public static void main(String [] args) {
new UnitTest().testDoSomething(new Unit1(), new Unit2());
}
static class Unit1 {
protected void doSomething() {} // made protected
}
static class Unit2 {
protected void doSomething() {} // made protected
}
static class UnitTest {
// ---> no changes needed although UnitTest doesn't subclass
// ...and, hey, if I'd have to subclass... which one of Unit1, Unit2?
void testDoSomething(Unit1 unit1, Unit2 unit2) {
unit1.doSomething();
unit2.doSomething();
}
}
}
Widzisz, mogę zachować kod testu jednostkowego w ExampleEvolved
niezmienionej postaci, ponieważ chronione metody są dostępne z tego samego pakietu, nawet jeśli dostęp do obiektu nie jest podklasą .
Potrzebne mniej zmian => bezpieczniejsza modyfikacja; w końcu zmieniłem tylko modyfikatory dostępu i nie modyfikowałem metod Unit1.doSomething()
i działań Unit2.doSomething()
, więc naturalne jest oczekiwanie, że kod testu jednostkowego będzie kontynuowany bez modyfikacji.
protected
była tylko podklasa? Szczerze mówiąc, przez długi czas miałem wrażenie, że takie jest zachowanieprotected
jest tylko klasą i podklasą iinternal
obejmuje bibliotekę / pakiet. Ma takżeprotected internal
odpowiednik Javyprotected
.IMHO, to była zła decyzja projektowa w Javie.
Po prostu spekuluję, ale myślę, że chcieli, aby poziomy dostępu były ściśle określone: prywatny - „pakiet” - chroniony - publiczny. Nie chcieli mieć hierarchii, w której niektóre pola byłyby dostępne dla pakietu, ale nie dla podklas, niektóre dla podklas, ale nie dla pakietu, a niektóre oba.
Mimo to, moim skromnym zdaniem, powinni byli pójść w drugą stronę: Powiedzieli, że chroniony jest widoczny tylko dla klasy i podklas, a ten pakiet jest widoczny dla klasy, podklas i pakietu.
Często mam dane w klasie, które muszą być dostępne dla podklas, ale nie dla reszty pakietu. Trudno mi wymyślić przypadek, w którym sytuacja była odwrotna. Miałem kilka rzadkich okazji, w których miałem pakiet powiązanych ze sobą klas, które muszą dzielić się danymi, ale które powinny chronić te dane przed spakowaniem. W porządku, umieść je w pakiecie itp. Ale nigdy nie miałem przypadku, w którym chciałbym, aby pakiet współdzielił dane, ale chcę, aby nie był przechowywany w podklasach. Okej, mogę sobie wyobrazić nadchodzącą sytuację. Podejrzewam, że jeśli ten pakiet jest częścią biblioteki, która może być rozszerzona o klasy, o których nic nie wiem, z tych samych powodów, dla których uczynię dane w klasie prywatnymi. Ale o wiele bardziej powszechne jest udostępnianie danych tylko klasie i jej dzieciom.
Jest wiele rzeczy, które zachowuję prywatność między mną a moimi dziećmi i nie dzielimy się z sąsiadami. Jest bardzo niewiele rzeczy, które trzymam w tajemnicy między mną a sąsiadami i nie dzielę się nimi z moimi dziećmi. :-)
źródło
Dobrym przykładem, który natychmiast przychodzi mi na myśl, są klasy narzędzi, które są często używane w pakiecie, ale nie chcesz publicznego dostępu (za kulisami ładowanie obrazu z dysku, klasy tworzenia uchwytów / niszczenia itp. .) zamiast tworzenia wszystko [odpowiednik Java
friend access modifier or idiom
wC++
] każdej innej klasy wszystko jest automatycznie dostępna.źródło
Przypadki użycia dla modyfikatora dostępu chronionego / dostępu do pakietu są podobne do przypadków dla modyfikatorów dostępu znajomych w C ++.
Jednym z przypadków użycia jest implementacja wzorca Memento .
Obiekt memento musi uzyskać dostęp do wewnętrznego stanu obiektu, aby go zatrzymać, aby służyć jako punkt kontrolny dla operacji cofania.
Deklarowanie klasy w tym samym pakiecie jest jednym z możliwych sposobów osiągnięcia wzorca Memento, ponieważ Java nie ma modyfikatora dostępu „zaprzyjaźnionego” .
źródło
symetria?
Rzadko potrzebny jest taki dostęp, dlatego dostęp domyślny jest tak rzadko używany. Ale czasami frameworki chcą tego dla wygenerowanego kodu, w którym klasy otoki są umieszczane w tym samym pakiecie, który oddziałuje bezpośrednio z twoimi klasami, przechodząc do członków zamiast do akcesorów ze względów wydajnościowych. Pomyśl GWT.
źródło
Hierarchia hermetyzacji Java jest dobrze zdefiniowana:
Klasa -> Pakiet -> Dziedziczenie
„Protected” jest po prostu słabszą formą prywatności niż domyślny pakiet, zgodnie z decyzją projektantów Java. Dostęp do domyślnych elementów pakietu jest ograniczony do podzbioru podmiotów, które mają dostęp do elementów chronionych.
Z punktu widzenia matematyki i implementacji sensowne jest posiadanie zestawów jednostek, które mają dostęp do zagnieżdżanych elementów. (Nie można zagnieździć zestawu dostępu do pakietu w zestawie chronionego dostępu, ponieważ klasy mogą dziedziczyć z innych pakietów).
Z koncepcyjnego punktu widzenia sensownie jest mieć coś w java.util, aby być „bardziej przyjaznym” z inną klasą w pakiecie niż coś w com.example.foo.bar, który to podklasuje. W pierwszym przypadku klasy prawdopodobnie zostaną napisane przez tego samego autora lub co najmniej programistów z tej samej organizacji.
źródło