Mam pewne interfejsy, które zamierzam wdrożyć w przyszłości przez strony trzecie, i sam zapewniam podstawowe wdrożenie. Będę używał tylko kilku, aby pokazać przykład.
Obecnie są one zdefiniowane jako
Pozycja:
public interface Item {
String getId();
String getName();
}
ItemStack:
public interface ItemStackFactory {
ItemStack createItemStack(Item item, int quantity);
}
ItemStackContainer:
public interface ItemStackContainer {
default void add(ItemStack stack) {
add(stack, 1);
}
void add(ItemStack stack, int quantity);
}
Teraz Item
i ItemStackFactory
ja mogę absolutnie przewidzieć trochę innej firmy, która chciałaby przedłużyć go w przyszłości. ItemStackContainer
Mogę również zostać rozszerzony w przyszłości, ale nie w sposób, który mogę przewidzieć, poza moją domyślną implementacją.
Teraz staram się, aby ta biblioteka była jak najbardziej niezawodna; jest to wciąż na wczesnym etapie (pre-pre-alfa), więc może to być akt nadmiernej inżynierii (YAGNI). Czy to odpowiednie miejsce do używania leków generycznych?
public interface ItemStack<T extends Item> {
T getItem();
int getQuantity();
}
I
public interface ItemStackFactory<T extends ItemStack<I extends Item>> {
T createItemStack(I item, int quantity);
}
Obawiam się, że może to spowodować, że implementacje i użytkowanie będą trudniejsze do odczytania i zrozumienia; Myślę, że to zalecenie, aby unikać zagnieżdżania generyków tam, gdzie to możliwe.
Odpowiedzi:
W interfejsie używa się ogólnych, gdy implementacja może być również ogólna.
Na przykład każda struktura danych, która może akceptować dowolne obiekty, jest dobrym kandydatem na ogólny interfejs. Przykłady:
List<T>
iDictionary<K,V>
.Każda sytuacja, w której chcesz poprawić bezpieczeństwo typu w uogólniony sposób, jest dobrym kandydatem na generyczne. A
List<String>
to lista, która działa tylko na ciągach.Każda sytuacja, w której chcesz zastosować SRP w sposób uogólniony i uniknąć metod specyficznych dla typu, jest dobrym kandydatem na leki generyczne. Na przykład PersonDocument, PlaceDocument i ThingDocument można zastąpić przez
Document<T>
.Wzorzec Fabryki abstrakcyjnej jest dobrym przypadkiem użycia ogólnego, podczas gdy zwykła Metoda Fabryczna po prostu tworzyłaby obiekty, które dziedziczą ze wspólnego, konkretnego interfejsu.
źródło
class StackFactory { Stack<T> Create<T>(T item, int count); }
. Mogę napisać przykład tego, co miałem na myśli przez „doprecyzowanie parametru typu w podklasie” w odpowiedzi, jeśli chcesz uzyskać dodatkowe wyjaśnienia, chociaż odpowiedź byłaby tylko stycznie powiązana z pierwotnym pytaniem.Twój plan wprowadzenia ogólności do interfejsu wydaje mi się poprawny. Jednak twoje pytanie, czy byłby to dobry pomysł, czy nie, wymaga nieco bardziej skomplikowanej odpowiedzi.
Przez lata przeprowadziłem wywiady z wieloma kandydatami na stanowiska inżynierii oprogramowania w imieniu firm, dla których pracowałem, i zdałem sobie sprawę, że większość osób poszukujących pracy czuje się swobodnie, korzystając z klas ogólnych (na przykład standardowe klasy kolekcji), ale nie z pisaniem klas ogólnych. Większość nigdy nie napisała żadnej klasy ogólnej w swojej karierze i wygląda na to, że byliby całkowicie zagubieni, gdyby zostali poproszeni o napisanie jednej. Tak więc, jeśli narzucisz użycie generycznych na kogokolwiek, kto chce wdrożyć twój interfejs lub rozszerzyć podstawowe implementacje, możesz zniechęcać ludzi.
Możesz jednak spróbować dostarczyć zarówno ogólne, jak i nie-ogólne wersje swoich interfejsów i podstawowych implementacji, aby ludzie, którzy tego nie robią, mogli żyć szczęśliwie w swoim wąskim, obsadzonym czcionkami świecie, podczas gdy ludzie, którzy generics mogą czerpać korzyści z szerszego rozumienia języka.
źródło
Twoja decyzja została podjęta za ciebie. Jeśli nie podasz ogólnych, będą musieli ulepszyć ich implementację za
Item
każdym razem, gdy używają:Powinieneś przynajmniej stworzyć
ItemStack
ogólny sposób, jaki sugerujesz. Najgłębszą korzyścią ze stosowania leków generycznych jest to, że prawie nigdy nie musisz niczego rzucać, a oferowanie kodu, który zmusza użytkowników do rzucania, jest przestępstwem kryminalnym.źródło
Wow ... Mam zupełnie inne podejście do tego.
Jest to błąd. Nie powinieneś pisać kodu „na przyszłość”.
Interfejsy są również zapisywane z góry na dół. Nie patrzysz na klasę i nie mówisz: „Hej, ta klasa mogłaby całkowicie zaimplementować interfejs, a potem mogę użyć tego interfejsu także gdzie indziej”. To niewłaściwe podejście, ponieważ tylko klasa, która korzysta z interfejsu, naprawdę wie, jaki powinien być interfejs. Zamiast tego powinieneś pomyśleć: „W tym miejscu moja klasa potrzebuje zależności. Pozwól mi zdefiniować interfejs dla tej zależności. Kiedy skończę pisać tę klasę, napiszę implementację tej zależności”. To, czy ktoś kiedykolwiek ponownie użyje twojego interfejsu i zastąpi Twoją domyślną implementację własną, jest zupełnie nieistotne. Mogą nigdy nie zrobić. Nie oznacza to, że interfejs był bezużyteczny. Sprawia, że Twój kod działa zgodnie z zasadą Inwersji Kontroli, dzięki czemu Twój kod jest bardzo rozszerzalny w wielu miejscach ”
źródło
Myślę, że używanie leków generycznych w twojej sytuacji jest zbyt skomplikowane.
Jeśli wybierzesz ogólną wersję interfejsów, projekt to wskazuje
Te domniemane założenia i ograniczenia są bardzo ważnymi rzeczami, na które należy uważnie zdecydować. Tak więc, zwłaszcza na wczesnym etapie, nie należy wprowadzać tych przedwczesnych projektów. Pożądany jest prosty, łatwy do zrozumienia, wspólny projekt.
źródło
Powiedziałbym, że zależy to głównie od tego pytania: jeśli ktoś musi rozszerzyć funkcjonalność
Item
iItemStackFactory
,W pierwszym przypadku nie ma potrzeby stosowania leków generycznych, w drugim prawdopodobnie tak jest.
źródło
Być może możesz mieć hierarchię interfejsów, w której Foo to jeden interfejs, a Bar to inny. Ilekroć typ musi być jednym i drugim, jest to FooAndBar.
Aby uniknąć przekroczenia granicy, wpisz FooAndBar tylko wtedy, gdy jest to konieczne, i przechowuj listę interfejsów, które nigdy niczego nie rozszerzają.
Jest to o wiele wygodniejsze dla większości programistów (najbardziej lubią sprawdzanie typu lub nigdy nie radzą sobie z pisaniem statycznym jakiegokolwiek rodzaju). Bardzo niewiele osób napisało własną klasę leków ogólnych.
Również jako uwaga: interfejsy określają, jakie możliwe akcje można uruchomić. Powinny to być czasowniki, a nie rzeczowniki.
źródło