Jak enkapsulować klasy wewnętrzne w interfejsie API napisanym w Javie?

9

Musimy napisać bibliotekę. Oczywiście powinien mieć tylko bardzo mały interfejs API (tak szeroki, jak to konieczne, tak mały, jak to możliwe). Wnętrza biblioteki są nieco skomplikowane. Dlatego potrzebują strukturyzacji.

Dla struktury widzę obecnie dwa sposoby:

1. użyj pakietów.

zalety: biblioteka może być uporządkowana. Wszystko na swoim miejscu.

Wady: użycie klas przez granice pakietów wymaga klas publicznych, a zatem poszerza API całej biblioteki.

2. użyj statycznych klas wewnętrznych, wszystko w jednym pakiecie.

plusy: potrzebne są bardzo małe rzeczy publiczne (klasy, metody itp.).

Wady: Klasy są ukryte tylko po to, aby je uporządkować. Byłby to jeden z niewielu przypadków użycia, w których zastosowano tak wiele statycznych klas wewnętrznych. Programiści nie są do tego przyzwyczajeni i mogą je przeoczyć.


Czy jest lepszy sposób na uzyskanie małego API w dobrze zorganizowanej bibliotece?

Edycja: Zapomniałem wspomnieć: to dla biblioteki Androida. Dlatego nie ma java9.

Markus Frauenfelder
źródło
Dlaczego twoje klasy wewnętrzne muszą być statyczne?
David Arno,
Czy klasy wewnętrzne nie zwiększają jednostek kodu? Mam na myśli, że klasa z kilkoma klasami wewnętrznymi może stać się bardzo długa.
Tulains Córdova,
2
@ TulainsCórdova, dobra uwaga, czytam termin „wewnętrzny” jako „wewnętrzny”, który, jak myślę, Java nazywa „pakietem prywatnym”. Normalnym sposobem na utworzenie biblioteki lib jest z pewnością jeden pakiet zawierający kilka klas publicznych, przy czym wszystko inne jest wewnętrzne / pakiet-prywatny.
David Arno,
Cześć @frmarkus. Próbowałem ponownie napisać tytuł pytania, aby był bardziej szczegółowy i unikał „niejasnego pytania”. Możesz go ponownie edytować, jeśli uważasz, że to nieprawdziwe pytanie.
Andres F.,
1
@ TulainsCórdova: Tak, jednostki kodu są zbyt duże (3k linii kodu na plik). To kolejny ważny problem tego sposobu strukturyzacji.
Markus Frauenfelder,

Odpowiedzi:

1

Widzę, co robisz z 2. Używasz klas jako pakietów, a pakietów jako modułów, dzięki czemu możesz izolować się w pakiecie, ale nadal organizować się w pakiecie za pomocą klas.

To bardzo sprytne. Uważaj na sprytne.

To zmusi cię do zablokowania wielu klas w tym samym pliku źródłowym (co możesz preferować), a ścieżka będzie zawierać dodatkowe wielkie litery.

Zmusi Cię to również do napisania dowolnego kodu testowego w pakiecie, chyba że użyjesz refleksji, aby włamać się z zewnątrz.

Poza tym to zadziała. To będzie po prostu dziwne.

Ludzie są bardziej przyzwyczajeni do klas wewnętrznych, takich jak EntrySet w Hashtable. Jest prywatny, więc nie mogę go utworzyć, ale implementuje interfejs publiczny, więc po prostu rozmawiam z nim przez interfejs i mam coś dla siebie.

Ale opisujesz zajęcia, z którymi nie chcesz, żebym rozmawiał nawet przez interfejs. Więc nie ma dla mnie interfejsu. Oznacza to, że nie mam na co patrzeć i być zdezorientowanym (chyba że podasz mi źródło).

Największym problemem, jaki przewiduję, jest to mylące początkujące utrzymanie interfejsu API. Możesz rzucać na nie dokumentacją i komentarzami, ale nie bądź wielka, jeśli nie czytają ani nie ufają żadnej z nich.

Stworzyłeś kolejny wzorzec, który uzupełnia brak języka. Java nie ma modyfikatora dostępu, który zapewnia dostęp do grupy pakietów. Słyszałem, że zaproponowano modyfikator dostępu do „modułu”, ale nie widzę żadnych oznak tego.

Domyślny modyfikator dostępu (bez modyfikatora) jest prawdopodobnie tym, czego tu użyjesz, chyba że nie przeszkadza ci wkradanie się przez dziedziczenie, w którym to przypadku jest chronione.

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 

To, czego naprawdę chcesz, to dostęp do modułu. W ten sposób możesz przechowywać testy w jednym pakiecie, a kod w innym. Niestety nie mamy tego w Javie.

Większość ludzi po prostu robi 1 i rozwija API. Właściwe użycie interfejsów zmniejsza presję na wdrożenie.

Hakowanie tego, co chcesz, jest jeszcze brzydsze. Rzuć okiem na stos wywołań i zgłaszaj wyjątek za każdym razem, gdy to, co nazywa cię, pochodzi z pakietu, którego nie lubisz. Eeew.

candied_orange
źródło
3

Jak najbardziej, podziel swój kod na pakiety. Przejdzie długą drogę do poprawy łatwości konserwacji.

Aby użytkownicy końcowi nie mieli dostępu do rzeczy, których nie powinni, musisz rozdzielić interfejs API na interfejs i implementację.

Możesz to zrobić bardzo dosłownie, definiując cały interfejs API w kategoriach interfejsów Java i udostępniając pewną liczbę klas fabrycznych do tworzenia obiektów implementacyjnych. To właśnie robi JDBC.

Lub możesz to zrobić za pomocą konwencji, tworząc internalpakiety i dokumentując je jako zależne od implementacji i mogą ulec zmianie bez ostrzeżenia lub zgodności z poprzednimi wersjami.

kdgregory
źródło
1
Niestety jest to kompletny zakaz. To pozostawia wiele klas publicznych. Mogą mieć wskazówki (jako fabryki lub „wewnętrzne”). Ale muszę zastosować do nich dodatkowe narzędzia (do wszystkich klas publicznych), to po prostu nie jest dobre.
Markus Frauenfelder,
@frmarkus - Myślę, że (1) oczekujesz zbyt wiele kontroli dostępu lub (2) musisz przemyśleć strukturę pakietu, aby klasy z jednego pakietu nie musiały polegać na klasach z innego pakiet (jeśli możesz zachować wszystko jako statycznie zagnieżdżone klasy, nie powinno to być trudne).
kdgregory,