Jestem nowy w zespole pracującym nad dość dużym projektem z wieloma komponentami i zależnościami. Dla każdego komponentu istnieje interfaces
pakiet, w którym umieszczone są dostępne interfejsy dla tego komponentu. Czy to dobra praktyka?
Moją zwykłą praktyką zawsze było to, że interfejsy i implementacje odbywały się w tym samym pakiecie.
Odpowiedzi:
Umieszczenie zarówno interfejsu, jak i implementacji jest powszechne i nie wydaje się być problemem.
Weźmy na przykład Java API - większość klas ma oba interfejsy i ich implementacje zawarte w tym samym pakiecie.
Weźmy na przykład
java.util
pakiet:Zawiera interfejsy, takie jak
Set
,Map
,List
, a także uwzględniając implementacje takich jakHashSet
,HashMap
iArrayList
.Ponadto Javadoc są zaprojektowane tak, aby dobrze działały w takich warunkach, ponieważ rozdziela dokumentację na widoki Interfejsy i Klasy podczas wyświetlania zawartości pakietu.
Posiadanie pakietów tylko dla interfejsów może być w rzeczywistości trochę przesadzone, chyba że istnieje ogromna liczba interfejsów. Jednak rozdzielanie interfejsów na osobne pakiety tylko po to, by to robić, brzmi jak zła praktyka.
Jeśli konieczne jest odróżnienie nazwy interfejsu od implementacji, można by zastosować konwencję nazewnictwa, aby ułatwić identyfikację interfejsów:
Poprzedź nazwę interfejsu rozszerzeniem
I
. To podejście jest stosowane w przypadku interfejsów w środowisku .NET. Dość łatwo byłoby stwierdzić, żeIList
jest to interfejs dla listy.Użyj
able
przyrostka - . Takie podejście jest często postrzegane w Java API, takich jakComparable
,Iterable
iSerializable
aby wymienić tylko kilka.źródło
W przypadku każdego języka połączenie ich w jednym pakiecie jest w porządku. Ważne jest to, co jest wystawione na świat zewnętrzny i jak wygląda z zewnątrz. Nikt nie będzie wiedział ani nie dbał o to, czy implementacja znajduje się w tym samym pakiecie, czy nie.
Spójrzmy na ten konkretny przypadek.
Jeśli masz wszystkie publiczne rzeczy w jednym pakiecie, a prywatne rzeczy w innym pakiecie, które nie są publicznie ujawniane, klient biblioteki widzi jeden pakiet. Jeśli przenosisz rzeczy prywatne do paczki z rzeczami publicznie ujawnionymi, ale nie ujawniasz ich z paczki, klient widzi dokładnie to samo.
Tak więc ma to zapach reguły bez dobrego powodu: to podejmowanie decyzji na podstawie tego, co jest publicznie widoczne, a decyzja ta nie ma żadnego wpływu na to, co jest publicznie widoczne.
To powiedziawszy, jeśli w jakimkolwiek konkretnym przypadku wydaje się dobrym pomysłem podzielenie interfejsu i implementacji na oddzielne pakiety, zrób to od razu. Przychodzi mi do głowy to, że pakiet jest ogromny lub masz alternatywną implementację, którą możesz chcieć połączyć zamiast standardowej.
źródło
friend
(C ++) lubinternal
(C #).W wielu frameworkach, takich jak OSGi, prawie musisz. Myślę, że to promuje luźniejsze połączenie, na opakowaniu zamiast na poziomie słoika.
źródło
Jednym z argumentów przemawiających za umieszczaniem interfejsów w różnych pakietach jest to, że łatwiej jest tworzyć słoiki „api”, które można rozprowadzać wśród konsumentów produktu lub usługi. Jest to całkowicie możliwe dzięki połączeniu interfejsów i implementacji, ale prostsze jest tworzenie skryptów, jeśli znajdują się one w różnych pakietach.
źródło
Książka Practical Software Engineering: A Case-Study Approach opowiada się za umieszczaniem interfejsów w oddzielnych projektach / pakietach.
Architektura PCMEF +, o której mówi książka, ma następujące zasady:
Opis zasady # 3 i # 7 wyjaśnia, dlaczego jest to dobry pomysł:
Zobacz ten link: http://comp.mq.edu.au/books/pse/about_book/Ch9.pdf
źródło
Tak, to bardzo dobra praktyka, ponieważ umożliwia opublikowanie interfejsu bez publikowania konkretnej implementacji. To powiedziawszy, jeśli nie musisz publikować zewnętrznych interfejsów, nie ma problemu z umieszczeniem definicji interfejsów w tym samym pakiecie co implementacja.
źródło
Robimy to tam, gdzie pracuję (tj. Umieszczamy interfejs w jednym pakiecie, a implementację w innym), a główną zaletą, jaką z tego uzyskujemy, jest możliwość łatwego przełączania się między implementacjami.
źródło
Nie mam dużego doświadczenia w Javie, ale lubię to robić jako dobrą praktykę w C # / .NET, ponieważ pozwala to na przyszłą rozbudowę, gdzie zespoły z konkretnymi klasami, które implementują interfejsy, mogą nie być dystrybuowane aż do klienta, ponieważ są one obsługiwane przez pośrednika fabryki lub przez sieć w scenariuszu usługi sieci Web.
źródło
Zwykle umieszczam interfejsy z implementacją, ale widzę, dlaczego możesz chcieć je oddzielić. Powiedzmy na przykład, że ktoś chciałby ponownie zaimplementować klasy na podstawie twoich interfejsów, potrzebowałby jar / lib / etc z twoją implementacją, a nie tylko interfejsami. Mając je oddzielne, możesz po prostu powiedzieć „Oto moja implementacja tego interfejsu” i skończyć z tym. Jak powiedziałem, nie to, co robię, ale w pewnym sensie rozumiem, dlaczego niektórzy mogą chcieć.
źródło
Robię to na projekcie i działa to dobrze z następujących powodów:
Oddzielnie spakowane interfejsy i implementacje są dla innego przypadku użycia niż dla różnych typów Map lub Set. Nie ma powodu, aby mieć pakiet tylko dla drzew (java.util.tree.Map, java.util.tree.Set). To tylko standardowa infrastruktura danych, więc połącz ją z innymi strukturami danych. Jeśli jednak masz do czynienia z grą logiczną, która ma naprawdę prosty interfejs do debugowania i ładny interfejs produkcyjny, jako część jej interfejsu możesz mieć com.your.app.skin.debug i com.your.app .skin.pretty. Nie umieszczałbym ich w tym samym pakiecie, ponieważ robią różne rzeczy i wiem, że skorzystałbym z jakiejś SmurfNamingConvention (DebugAttackSurface, DebugDefenceSurface, PrettyAttackSurface itp.), Aby utworzyć nieformalną przestrzeń nazw dla tych dwóch, gdyby były w tym samym pakiecie.
Moim obejściem problemu znajdowania powiązanych interfejsów i implementacji, które są w oddzielnych pakietach, jest przyjęcie konfiguracji nazewnictwa dla moich pakietów. Np. Mogę mieć wszystkie moje interfejsy w com.your.app.skin.framework i wiedzieć, że inne pakiety na tym samym poziomie drzewa pakietów są implementacjami. Wadą jest to, że jest to niekonwencjonalna konwencja. Szczerze, zobaczę jak dobry jest ten konwent za 6 miesięcy :)
Nie używam tej techniki religijnie. Istnieją interfejsy, które mają sens tylko w konkretnej implementacji. Nie umieszczam ich w pakiecie ramowym. Jest kilka pakietów, w których nie wygląda na to, żebym utworzył 40 różnych klas implementacji, więc nie przejmuję się.
Moja aplikacja korzysta z guice i ma bardzo wiele interfejsów.
Pytania dotyczące projektu programu zwykle wiążą się z zaletami i wadami i nie ma jednej odpowiedzi, która byłaby odpowiednia dla wszystkich. Na przykład, dlaczego miałbyś kiedykolwiek używać tej techniki w małym 200-liniowym programie? Ma to sens dla mnie, biorąc pod uwagę moje inne wybory architektoniczne, dla mojego konkretnego problemu. :)
źródło
Wolę je w tym samym opakowaniu. Tworzenie pakietu specjalnie dla interfejsów nie ma sensu. Jest to szczególnie zbędne dla zespołów, które po prostu uwielbiają swoje przedrostki i sufiksy interfejsu (np. „I” i „Impl” w przypadku języka Java).
Jeśli istnieje potrzeba opublikowania zestawu interfejsów jako publicznego interfejsu API, bardziej sensowne jest przechowywanie ich w całkowicie oddzielnym projekcie i tworzenie zależności między projektami. Ale wszystko sprowadza się do preferencji i wygody sytuacji, jak przypuszczam.
źródło
Umieść je w pakietach, które odzwierciedlają Twoje projekty. Łączenie interfejsów i implementacji razem, jeśli są częścią tego samego projektu, jest dobre i powszechne, ale jeśli piszesz API, prawdopodobnie ktoś inny wybierze nazwę pakietu odpowiednią dla swojego projektu.
Ogólnie rzecz biorąc, jeśli dotyczy tego samego projektu, nie widzę żadnej korzyści z utrzymywania interfejsów w oddzielnym pakiecie od ich implów. Jeśli robi się zagracony, mogą wystąpić inne problemy z nazwami pakietów, niezależnie od układu interfejsu.
źródło
Myślę, że ważne jest, aby zauważyć, że dla frameworka OSGi ładniej jest, że są one w różnych pakietach, dzięki czemu można łatwo wyeksportować cały pakiet, ukrywając pakiety implementacyjne.
źródło
Istnieje wiele dobrych powodów, dla których należy umieścić interfejsy w oddzielnym pakiecie od implementacji. Na przykład możesz chcieć użyć kontenera, który obsługuje wstrzykiwanie zależności, do połączenia niezbędnych implementacji w czasie wykonywania, w którym to przypadku tylko interfejs musi być obecny w czasie kompilacji, a implementację można dostarczyć w czasie wykonywania. Alternatywnie, możesz chcieć udostępnić wiele implementacji (na przykład używając implementacji próbnych do testowania) lub wiele wersji konkretnej implementacji w czasie wykonywania (na przykład do testów A / B itp.). W tego rodzaju przypadkach wygodniej jest osobno spakować interfejs i implementację.
źródło