Słyszałem argument, że powinieneś używać najbardziej ogólnego interfejsu, abyś nie był przywiązany do konkretnej implementacji tego interfejsu. Czy ta logika ma zastosowanie do interfejsów takich jak java.util.Collection ?
Wolałbym raczej zobaczyć coś takiego:
List<Foo> getFoos()
lub
Set<Foo> getFoos()
zamiast
Collection<Foo> getFoos()
W ostatnim przypadku nie wiem, z jakim zestawem danych mam do czynienia, natomiast w pierwszych dwóch przypadkach mogę poczynić pewne założenia dotyczące kolejności i wyjątkowości. Czy java.util.Collection ma użyteczność poza byciem logicznym rodzicem zarówno dla zestawów, jak i list?
Jeśli natrafiłeś na kod, który używał Collection podczas przeglądania kodu, w jaki sposób ustaliłbyś, czy jego użycie jest uzasadnione, i jakie sugestie sugerowałbyś, aby zastąpić go bardziej szczegółowym interfejsem?
Collection<List<?>>
? Mów o kodowaniu horroru!Odpowiedzi:
Abstrakcje żyją dłużej niż implementacje
Ogólnie rzecz biorąc, im bardziej abstrakcyjny jest projekt, tym dłużej będzie on przydatny. Ponieważ kolekcja jest bardziej abstrakcyjna niż interfejsy, projekt interfejsu API oparty na kolekcji jest bardziej przydatny niż projekt oparty na liście.
Nadrzędną zasadą jest jednak stosowanie najbardziej odpowiedniej abstrakcji . Więc jeśli twoja kolekcja musi obsługiwać uporządkowane elementy, to upoważnij Listę, jeśli nie ma duplikatów, upoważnij Zestaw i tak dalej.
Uwaga na temat ogólnego projektu interfejsu
Ponieważ interesuje Cię korzystanie z interfejsu Collection w połączeniu z lekami generycznymi, możesz skorzystać z następujących wskazówek. Efektywna Java autorstwa Joshua Bloch zaleca następujące podejście przy projektowaniu interfejsu, który będzie polegał na rodzajach ogólnych: Producenci Rozszerz, Konsumenci Super
Jest to również znane jako reguła PECS . Zasadniczo, jeśli ogólne zbiory produkujące dane są przekazywane do twojej klasy, podpis powinien wyglądać następująco:
Zatem typem wejściowym może być E lub dowolna podklasa E (E jest zdefiniowana jako zarówno sama superklasa, jak i podklasa w języku Java).
I odwrotnie, ogólna kolekcja przekazywana do konsumpcji danych powinna mieć taką sygnaturę:
Metoda będzie prawidłowo radzić sobie z każdym nadklasie E. Ogólnie, stosując takie podejście sprawi, że interfejs mniej zaskakujące dla użytkowników, ponieważ będziesz w stanie przejść w
Collection<Number>
aCollection<Integer>
i mieć je traktować poprawnie.źródło
Collection
Interfejs, a najbardziej liberalna formaCollection<?>
, doskonale nadaje się do parametrów , które akceptują. Oparte na zależności od zastosowania w samej bibliotece Java jest bardziej powszechny jako typ parametru niż typ zwracany.W przypadku typów zwrotów uważam, że twój punkt jest poprawny: jeśli ludzie mają uzyskać dostęp do niego, powinni znać kolejność (w sensie Big-O) wykonywanej operacji. Chciałbym powtórzyć
Collection
zwrócony i dodać go do innej kolekcji, ale zadzwonienie wydaje się nieco szalonecontains
, nie wiedząc, czy jest to operacja O (1), O (log n), czy O (n). Oczywiście, tylko dlatego, że maszSet
, nie oznacza, że jest to zbiór mieszany lub posortowany, ale w pewnym momencie przyjmiesz założenia, że interfejs został racjonalnie zaimplementowany (a następnie będziesz musiał przejść do planu B, jeśli twoje założenie jest niepoprawny).Jak wspomina Tom, czasami trzeba zwrócić a
Collection
, aby zachować enkapsulację: Nie chcesz, aby wyciekły szczegóły implementacji, nawet jeśli możesz zwrócić coś bardziej szczegółowego. Lub, w przypadku, o którym wspomniał Tom, możesz zwrócić bardziej konkretny pojemnik, ale wtedy musisz go zbudować.źródło
Spojrzałbym na to z zupełnie przeciwnego punktu widzenia i zapytałem:
Bardzo łatwo to uzasadnić. Korzystasz z Listy, gdy potrzebujesz funkcji, która nie jest oferowana przez Kolekcję. Jeśli nie potrzebujesz tej dodatkowej funkcjonalności - jakie masz uzasadnienie? (I nie kupię „Wolałbym to zobaczyć”)
Istnieje wiele przypadków, w których będziesz używać kolekcji do celów tylko do odczytu, zapełniając ją naraz, iterując ją całkowicie - czy potrzebujesz indeksować ją ręcznie?
Dać prawdziwy przykład. Powiedzmy, że wykonuję proste zapytanie w bazie danych. (
SELECT id,name,rep FROM people WHERE name LIKE '%squared'
) Odbieram odpowiednie dane, wypełniam obiekty Person i umieszczam je w PersonList)Jakie miałbym więc uzasadnienie dla tych dodatkowych metod? (które i tak nie zostałyby wprowadzone do mojej Listy Personalnej)
źródło