Platforma Eclipse
Praca z interfejsem API
Wersja 0.15 - ostatnia aktualizacja: 30 maja 2001 godzina 12:00
Poniżej przedstawiono reguły korzystania przez klientów z interfejsu API oraz innych elementów platformy Eclipse.
Co to jest interfejs API?
Platforma Eclipse definiuje elementy API na użytek swoich klientów, czyli modułów dodatkowych tworzonych przez niezależnych producentów oprogramowania. Te moduły dodatkowe mogą z kolei zawierać definicje elementów API na użytek swoich klientów i tak dalej. Elementy API to zasoby przeznaczone do użytku publicznego - zawierają specyfikację określającą ich działanie i sposób użycia. Elementy API są obsługiwane przez zespół programistyczny platformy Eclipse, co oznacza, że poprawia on błędy implementacji w miejscach, w których jej działanie różni się od działania określonego przez specyfikację. Ponieważ wprowadzanie poważnych zmian w interfejsach API jest związane z dużymi kosztami, zespół programistyczny platformy Eclipse będzie próbował także rozwijać elementy API w kolejnych wersjach głównych.
Jak odróżnić elementy API od innych elementów?
Elementy API są dobrze udokumentowane i mają określoną specyfikację. W odróżnieniu od nich elementy inne niż elementy API składają się tylko z implementacji wewnętrznej i nie mają opublikowanej dokumentacji lub specyfikacji. Jeśli nie można znaleźć dokumentacji danego elementu, zwykle oznacza to, że nie należy on do interfejsu API.
Aby ten podział był bardziej jasny, kod bazowy platformy jest podzielony na pakiety elementów API i pakiety elementów innych niż API, a wszystkie elementy API są deklarowane w odpowiednich pakietach elementów API.
-
Pakiet API - pakiet kodu Java zawierający co najmniej jedną klasę lub interfejs API. Nazwy pakietów API są ogłaszane w dokumentacji danego komponentu. Tam, gdzie jest to możliwe, wszystkie pozostałe pakiety zawierające tylko implementacje mają w nazwie pakietu słowo internal. Nazwy pakietów API mogą występować w kodzie klienta. W przypadku platformy Eclipse są to:
-
org.eclipse.foo.* - na przykład org.eclipse.swt.widgets, org.eclipse.ui
lub org.eclipse.core.runtime
-
org.eclipse.foo.internal.* - pakiety elementów innych niż elementy API (zawierające implementację wewnętrzną)
-
org.eclipse.foo.examples.* - pakiety elementów innych niż elementy API (przykłady)
-
org.eclipse.foo.tests.* - pakiety elementów innych niż elementy API (pakiety testów)
-
Klasa lub interfejs API - klasa lub interfejs public w pakiecie elementów API albo składowa klasy lub interfejsu public lub protected obecna w innej klasie lub interfejsie API przez deklarację lub dziedziczenie.
Nazwy klas i interfejsów API mogą występować w kodzie klienta.
-
Metoda lub konstruktor API - metoda lub konstruktor public albo protected obecny w klasie lub interfejsie API przez deklarację lub dziedziczenie. Nazwy metod API mogą występować w kodzie klienta.
-
Pole API - pole public lub protected obecne w klasie lub interfejsie API przez deklarację lub dziedziczenie. Nazwy pól API mogą występować w kodzie klienta.
Pozostałe elementy są traktowane jako implementacja wewnętrzna, która jest niedostępna dla wszystkich klientów. Poprawny kod klienta nie może zawierać odwołań do nazw elementów innych niż elementy API. Nie można odwoływać się do tych nazw nawet przy użyciu introspekcji Java. W niektórych przypadkach niedozwolone odwołania są odrzucane przy użyciu reguł dostępności języka Java.
Jest jednak wiele sytuacji, w których taka możliwość nie istnieje. Przestrzeganie jednej prostej reguły pozwala całkowicie uniknąć tego problemu:
-
Należy korzystać wyłącznie z oficjalnie udokumentowanych elementów API. Można odwoływać się tylko do tych pakietów, które opisano w opublikowanej dokumentacji Javadoc interfejsu API danego komponentu.
Nigdy nie można odwoływać się do pakietu należącego do innego komponentu, który ma w nazwie słowo internal, ponieważ takie pakiety nigdy nie są interfejsami API. Nigdy też nie należy odwoływać się do pakietu pozbawionego dokumentacji Javadoc interfejsu API, ponieważ on także nie jest interfejsem API.
Reguły ogólne
Specyfikacja elementów API jest generowana na podstawie komentarzy dokumentacji Javadoc w kodzie źródłowym Java danego elementu. Dla niektórych typów elementów specyfikacja ma formę kontraktu. Na przykład w przypadku metod kontrakt wiąże dwie strony: stronę wywołującą metodę i stronę implementującą metodę. W tej sytuacji obowiązuje żelazna zasada:
-
Należy wywiązywać się z warunków wszystkich kontraktów. Warunki kontraktów są opisane w opublikowanej dokumentacji Javadoc dotyczącej używanych elementów API.
Słowo "musi" użyte w kontrakcie dotyczącym elementu API oznacza, że dana strona ma obowiązek zadbać, aby dany warunek był zawsze spełniony. Niedopełnienie tego obowiązku będzie traktowane jak błąd programistyczny o nieokreślonych i niemożliwych do przewidzenia skutkach.
-
Należy wywiązywać się z kontraktów, w których użyto słowa "musi". Konieczne jest zwracanie szczególnej uwagi na te warunki, w których użyto słowa "musi".
Inne zdroworozsądkowe reguły:
-
Nie należy polegać na incydentalnym działaniu kodu. Incydentalne działanie to działanie zaobserwowane w wyniku eksperymentu lub w praktyce, które nie jest gwarantowane przez specyfikację elementu API.
-
Nie należy traktować wartości NULL jak obiektu. Wartość NULL oznacza raczej brak obiektu.
Można założyć, że wszystkie obiekty są różne od wartości NULL, dopóki w specyfikacji API nie stwierdzono inaczej.
-
Nie należy próbować oszukiwać przez stosowanie introspekcji Java. Używanie introspekcji Java w celu obejścia mechanizmów sprawdzających kompilatora Java nie daje żadnych korzyści. Nie ma żadnych dodatkowych kontraktów dotyczących elementów API, które regulowałyby stosowanie introspekcji. Technika ta zwiększa po prostu prawdopodobieństwo odwoływania się do elementów o nieznanej implementacji i nieprzewidywalnym działaniu.
-
Należy używać własnych pakietów. Nie można deklarować kodu w pakiecie należącym do innego komponentu. Zawsze trzeba deklarować własny kod w ramach własnych pakietów.
Wywoływanie metod public elementu API
W przypadku większości klientów największa część interfejsu API platformy Eclipse ma postać zestawu metod public zawartych w interfejsach i klasach API, które mogą być w razie potrzeby wywoływane przez klienta.
-
Należy zadbać, aby spełnione były warunki wstępne. Przed wywołaniem metody API konieczne jest spełnienie wszystkich jej warunków wstępnych. Na podobnej zasadzie podmiot wywołujący może bezpiecznie założyć, że bezpośrednio po powrocie z wywołania metody spełnione będą warunki końcowe metody.
-
Parametry o wartości NULL. Nie można przekazywać wartości NULL jako parametru do metody API, jeśli w dokumentacji nie stwierdzono wyraźnie, że parametr może mieć wartość NULL. Nieprzestrzeganie tej zasady jest prawdopodobnie najczęściej spotykanym błędem programistycznym.
-
Ograniczony zakres metod wywołujących. Nie można wywoływać metody API, której dokumentacja określa ograniczony zakres metod wywołujących, jeśli nie należy się do danego zakresu. W niektórych sytuacjach metody muszą należeć do publicznego interfejsu API, którego może używać tylko określona klasa podmiotów wywołujących (zwykle wewnętrznych). Wywołanie tego rodzaju metody w niewłaściwym momencie może mieć nieokreślone i nieprzewidywalne konsekwencje.
-
Metody do debugowania. Nie można wywoływać metody API oznaczonej jako przeznaczona tylko na potrzeby debugowania. Do tej kategorii należy na przykład większość metod toString().
-
Przechwytywanie parametrów. Po przekazaniu tablicy, kolekcji lub innego obiektu podlegającego mutacji jako parametru do metody API nie można już tego obiektu modyfikować. Takie postępowanie często jest przyczyną różnych kłopotów.
Tworzenie instancji klas API platformy
W przypadku niektórych klas konkretnych API nie każdy może tworzyć ich instancje.
Klasy API podlegają postanowieniom kontraktu dotyczącego tworzenia instancji, który określa warunki, na jakich mogą być tworzone instancje. Warunki kontraktu mogą dotyczyć także pewnych dodatkowych obowiązków w zakresie inicjowania (np. skonfigurowania konkretnej właściwości przed pełnym aktywowaniem instancji) lub związanych z cyklem życia (np. wywołania metody dispose() w celu zwolnienia zasobów systemu operacyjnego zajętych przez instancję). Klasy zaprojektowane tak, aby klienci mogli tworzyć ich instancje, są wyraźnie oznaczone w komentarzach klasy dokumentacji Javadoc (np. słowami Clients may instantiate - Klient może tworzyć instancje).
-
Ograniczony zakres podmiotów tworzących instancje. Nie można tworzyć instancji klas API, których dokumentacja określa je jako dostępne tylko dla konkretnych podmiotów, jeśli nie jest się jednym z tych podmiotów. W niektórych sytuacjach klasy muszą należeć do publicznego interfejsu API, którego może używać tylko określona klasa podmiotów (zwykle wewnętrznych). Wywołanie tego rodzaju klasy w niewłaściwym momencie może mieć nieokreślone i nieprzewidywalne konsekwencje.
Tworzenie podklas klas API platformy
Tylko niektóre klasy API mogą służyć jako podstawa do tworzenia podklas. Klasy API podlegają postanowieniom kontraktów dotyczących tworzenia podklas i określających warunki, na jakich można deklarować podklasy. Kontrakt taki dotyczy także obowiązków w zakresie inicjowania i związanych z cyklem życia. Klasy zaprojektowane tak, aby klienci mogli tworzyć ich podklasy, są wyraźnie oznaczone w komentarzach klasy dokumentacji Javadoc (np. słowami Clients may subclass - Klient może tworzyć podklasy).
-
Ograniczony zakres podmiotów tworzących podklasy. Nie można tworzyć podklas na podstawie klas API, które nie są do tego przeznaczone. Należy je traktować tak, jakby były zadeklarowane jako klasy final. Z tego powodu można je nazywać domniemanymi klasami final.
Wywoływanie metod protected interfejsu API
Wywoływanie dziedziczonych metod protected i public z podklasy jest zwykle dozwolone. Często jednak poprawne wykonanie tej operacji wymaga większej ostrożności niż wywoływanie metod public spoza hierarchii.
Przesłanianie metod API
Tylko niektóre metody public i protected interfejsu API zaprojektowano tak, aby można było je przesłaniać. Każda z metod API podlega postanowieniom kontraktu dotyczącego tworzenia podklasy, który określa warunki, na jakich dopuszczalne jest jej przesłanianie przez podklasę. Domyślnie przesłanianie jest niedozwolone.
Ważne jest, aby zapoznać się z warunkami kontraktu dotyczącego tworzenia podklas bieżącej implementacji przesłanianej metody. Przesłonięcie metody nie oznacza, że warunki tego kontraktu są automatycznie spełnione.
-
Nigdy nie należy przesłaniać metody public lub protected interfejsu API, jeśli nie jest to wyraźnie dozwolone. Z wyjątkiem przypadków, gdy jest to dozwolone, wszystkie metody należy traktować tak, jakby zadeklarowano je jako metody final. Z tego powodu można je nazywać domniemanymi metodami final.
Dozwolone sposoby przesłaniania:
- Implementacja - metoda abstract deklarowana w podklasie musi być implementowana przez podklasę konkretną.
- Rozszerzanie - metoda deklarowana w podklasie musi wywoływać metodę z nadklasy (tylko jeden raz).
- Reimplementacja - metoda deklarowana w podklasie nie może wywoływać metody z nadklasy.
- Przesłonięcie - metoda deklarowana w podklasie może w razie potrzeby swobodnie wywoływać metodę z nadklasy.
-
Spełnianie warunków końcowych. Należy zadbać o to, aby po powrocie z wywołania metody implementacja spełniała wszystkie warunki końcowe określone dla metody API.
-
Należy sprawdzać, czy są spełnione warunki wstępne. Nie należy zakładać, że warunki wstępne zdefiniowane dla danej metody API są na pewno spełnione i przechodzić bez ich sprawdzenia do części wykonawczej. Chociaż przyjęcie takiego założenia byłoby w pełni uprawnione, zwykle dobre efekty przynosi sprawdzenie warunków wstępnych (jeśli jest to wykonalne i nie wymaga zbyt dużych nakładów) w celu wykrycia ewentualnych niepoprawnie działających podmiotów wywołujących.
-
Wynik o wartości NULL. Nie można zwracać wartości NULL jako wyniku wywołania metody API, jeśli w dokumentacji interfejsu lub nadklasy nie stwierdzono wyraźnie, że wynikiem może być wartość NULL.
-
Zwracanie kopii. W wyniku wywołania metody API nie można zwracać niemożliwej do zastąpienia tablicy, kolekcji lub innego obiektu podlegającego mutacji. Zawsze należy zwracać kopię takiego obiektu, aby uniknąć problemów w przypadku podmiotów wywołujących, które mogą modyfikować dany obiekt.
Implementowanie interfejsów API platformy
Tylko niektóre interfejsy API zaprojektowano tak, aby możliwe było ich implementowanie przez klientów. Interfejsy API podlegają postanowieniom kontraktów określających warunki, na jakich mogą być implementowane. Interfejsy zaprojektowane tak, aby klienci mogli je implementować, są wyraźnie oznaczone w komentarzach klasy dokumentacji Javadoc (np. słowami Clients may implement - Klient może implementować). Klient może deklarować podinterfejs dla interfejsu API tylko wtedy, gdy jest to dozwolone.
-
Ograniczony zakres podmiotów implementujących. Nie można implementować interfejsów API, których dokumentacja określa je jako dostępne tylko dla konkretnych podmiotów, jeśli nie jest się jednym z tych podmiotów. W wielu sytuacjach interfejsy są używane w celu ukrycia wewnętrznych szczegółów implementacji.
Implementowanie metod public interfejsu API
Patrz: Przesłanianie metod API.
Dostęp do pól w klasach i interfejsach API
Pola API są dostępne dla klientów do odczytu i większość z nich jest polami final. Niektóre obiekty typu struct mogą mieć pola public, które nie będą polami final oraz będą mogły być odczytywane i zapisywane przez klientów (jeśli nie stwierdzono inaczej w dokumentacji).
-
Pola z wartością NULL. Nie można ustawiać pól API na wartość NULL, jeśli w dokumentacji nie stwierdzono wyraźnie, że jest to dozwolone.
Rzutowanie obiektów znanego typu API
Obiekt znanego typu API może być rzutowany tylko na inny typ API (lub rzutowany warunkowo przy użyciu operatora instanceof). Jest to możliwe pod warunkiem, że takie działanie jest dozwolone w przypadku danego interfejsu API.
-
Korzystanie z operatorów cast i instanceof. Nie można korzystać z wyrażeń zawierających operatory instanceof i cast w celu poszerzenia publicznie dostępnej charakterystyki obiektu poza zakres obsługiwany przez interfejs API.
Niewłaściwe użycie tych operatorów może wiązać się z ujawnieniem incydentalnych szczegółów implementacji, które nie są gwarantowane przez interfejs API.
Oczywiście rzutowanie obiektu na klasę lub interfejs nie będący elementem API jest zawsze błędem.
Nieprzestrzeganie reguł
Rozmyślne lub przypadkowe naruszenie reguł zawsze ma określone konsekwencje. Właściwie dla wszystkich zainteresowanych byłoby najlepiej, gdyby powołano specjalną jednostkę policji ścigającą osoby naruszające reguły pracy z interfejsami API. Niestety, nie jest to możliwe.
W tej sytuacji zgodność z regułami stosowania interfejsów API jest wymogiem swoistego kodeksu honorowego, według którego każdy klient jest sam odpowiedzialny za znajomość reguł i ich przestrzeganie.
Kontrakty związane z poszczególnymi elementami API określają granice, w ramach których działanie tych elementów jest obsługiwane i uznawane. Dalszy rozwój platformy Eclipse będzie postępował według ścieżek wyznaczanych przez kontrakty dotyczące elementów API. Wszystko, czego nie uwzględniono w tych kontraktach, nie jest gwarantowane i może ulec zmianie bez uprzedzenia oraz w dowolnym momencie (nawet podczas dystrybucji oprogramowania bez zmiany numeru wersji oraz w wersjach przeznaczonych dla różnych systemów operacyjnych). Kod klienta nieprzestrzegający tych reguł może działać błędnie w przypadku innej wersji platformy lub platformy z innym zestawem poprawek, po uruchomieniu w innym systemie operacyjnym, w środowisku z innym zestawem modułów dodatkowych, w innej perspektywie środowiska roboczego itd. W praktyce nikt nie zastanawia się, w jaki sposób naruszenie określonej reguły może obrócić się przeciwko niemu. Osoby decydujące się na ignorowanie reguł robią to na własną odpowiedzialność. W razie kłopotów nie powinny one potem oczekiwać niczego poza pełnymi współczucia słowami: "a przecież ostrzegaliśmy".
Natomiast kod modułu dodatkowego klienta, który opracowano zgodnie z powyższymi regułami, powinien poprawnie pracować, bez względu na używaną wersję platformy, stosowany zestaw poprawek, zainstalowany system operacyjny i inne moduły dodatkowe. Osobom przestrzegającym reguł platforma Eclipse zapewni stabilną podstawę, na której można tworzyć wiele nowych i atrakcyjnych produktów.