Rzeczywiście używasz wzorca szablonu. Istnieją dwie główne rzeczy można zrobić, aby „ukryć” dane z zewnętrznych użytkowników:
1) Zrozumieć swój access modifiers:
Access Levels
Modifier | Class | Package | Subclass | World |
--------------------------------------------------
public | Y | Y | Y | Y |
protected | Y | Y | Y | N |
no modifier | Y | Y | N | N |
private | Y | N | N | N |
brakuje zdecydowanych hakerów zastraszanie ich drogę w przeszłość tych zabezpieczeń z refleksji, mogą one utrzymuj przypadkowych programistów od przypadkowego wywoływania metod, które najlepiej trzymać z daleka. Kodiści to docenią. Nikt nie chce używać zagraconego API.
Obecnie b.doWork()
jest protected
. Więc będzie to widoczne tylko w twoim main()
, jeśli twoja main()
jest w klasie A
(to nie jest), podklasa B
(to nie jest), lub w tym samym pakiecie (nie jest jasne, nie wysłałeś żadnych wskazówek dotyczących pakietu (ów) twój kod znajduje się w).
To nasuwa się pytanie, kogo próbujesz chronić przed wyświetleniem b.doWork()
? Jeśli tworzony przez Ciebie pakiet jest czymś, co tylko Ty się rozwijasz, może nie być tak źle. Jeśli wiele osób tupie w tym pakiecie z wieloma innymi klasami, nie jest prawdopodobne, że będą one dokładnie zaznajomione z klasami A
i B
. Jest tak w przypadku, gdy nie chcesz, aby wszystko kręciło się tam, gdzie każdy może je zobaczyć. Tutaj byłoby miło zezwolić na dostęp tylko do klasy i podklasy. Ale nie ma modyfikatora, który umożliwia dostęp do podklasy bez umożliwienia dostępu do pakietu. Dopóki podklasy jesteś protected
jest najlepsze, co możesz zrobić. Mając to na uwadze, nie należy tworzyć pakietów z dużą liczbą klas. Utrzymuj paczki szczelne i skupione. W ten sposób większość ludzi będzie pracować poza pakietem, w którym rzeczy wyglądają ładnie i prosto.
Krótko mówiąc, jeśli chcesz, aby main()
nie zadzwonił pod numer b.doWork()
, umieść main()
w innym pakiecie.
package some.other.package
public final class Main {
...
}
2) Wartość tego następnego kawałka porad trudno będzie zrozumieć z aktualnym kodem ponieważ chodzi o rozwiązywanie problemów, które dopiero pojawią się, jeśli kod jest rozszerzony na. Ale to naprawdę jest ściśle związana z ideą ochrony ludzi przed widzeniem rzeczy jak b.doWork()
What does "program to interfaces, not implementations" mean?
W kodzie, co to oznacza to, że byłoby lepiej, gdyby główny wyglądał następująco:
public static void main(String[] args) {
A someALikeThingy = new B(); // <-- note use of type A
someALikeThingy.work(); //The name is silly but shows we've forgotten about B
//and know it isn't really just an A :)
}
Czemu? Co ma wspólnego korzystanie z typu A
i typu B
? Cóż, A
jest bliżej bycia interfejsem . Uwaga: tutaj nie chodzi mi tylko o to, co otrzymujesz, gdy używasz słowa kluczowego interface
.Mam na myśli typ B
jest konkretną implementacją typu A
i jeśli nie koniecznie muszę napisać kod przeciwko B
Naprawdę nie chciałbym, ponieważ kto wie, co dziwne nie A
rzeczy B
ma. Trudno to zrozumieć, ponieważ kod w głównej wersji jest krótki: & słodki. Ponadto, różne inne publiczne interfejsy nie są tak różne. Oba mają tylko jedną publiczną metodę: work()
. Wyobraź sobie, że main()
był długi i skomplikowany, Wyobraź sobie, że B
zawierał wiele różnych metod, które nie są dostępne pod A
. Teraz wyobraź sobie, że musisz stworzyć klasę C
, którą chcesz obsłużyć. Czy nie byłoby pocieszające zobaczyć, że podczas gdy główna była karmiona B
, o ile każda linia główna wie, że B
jest po prostu prostą rzeczą podobną do. Z wyjątkiem części po new
może to być prawda i zaoszczędzić od zrobienia czegoś na rzecz main()
, ale aktualizację tego new
. Czyż nie jest tak, że używanie silnie napisanego języka może czasami być miłe?
<rant>
nawet jeśli uważasz, że część aktualizacji po new
z B()
jest zbyt dużo pracy, że nie jesteś sam. Ta szczególna obsesja jest tym, co dało nam dependency injection movement, która zrodziła kilka frameworków, które naprawdę bardziej dotyczyły automatyzacji budowy obiektów niż zwykły zastrzyk, który każdy konstruktor może ci pozwolić.
</rant >
Aktualizacja:
widzę z waszych komentarzy, które są niechętne do tworzenia małych ciasnych pakiety. W takim przypadku rozważ porzucenie wzoru szablonu opartego na dziedziczeniu na rzecz wzorca strategii opartego na składzie. Nadal masz polimorfizm. Po prostu nie dzieje się za darmo. Niewiele więcej pisania kodu i pośrednictwa. Ale możesz zmienić stan nawet w czasie wykonywania.
Będzie to wydawać się sprzeczne z intuicją, ponieważ teraz doWork()
musi być publiczne. Jaka to jest ochrona? Teraz możesz ukryć B
całkowicie za lub nawet wewnątrz AHandler
. Jest to klasa przyjazna dla człowieka, która zawiera kod szablonu, który jest tylko szablonem, a tylko eksponuje work()
. A
może po prostu być interface
, więc AHandler
nadal nie wie, że ma B
. Takie podejście jest popularne wśród tłumu uzależnienia, ale z pewnością nie ma uniwersalnego uroku. Niektórzy robią to ślepo za każdym razem, gdy drażni ich wielu. Możesz przeczytać więcej na ten temat tutaj: Prefer composition over inheritance?
"jeden jest w stanie łatwo przypadkowo wywołać b.doWork()". Naprawdę? Jest chroniony, więc mogą go wywoływać tylko instancje A lub B. –
Niestety, ponieważ jest on chroniony, wszystkie klasy z pakietu mogą go wywoływać. Dzielenie pakietów jest drogą do zrobienia, ale wolałbym unikać pakietów 1- lub 2-klasowych. –
Nie! Opisujesz domyślny modyfikator dostępu (tzn. Brak). Chronione oznacza, że "tylko ja lub klasy, które mnie przedłużają, mogą z tego korzystać" –