Чи є вагомі причини використовувати інтерфейс колекції Java?


11

Я чув аргумент, що ви повинні використовувати найбільш загальний доступний інтерфейс, щоб ви не були прив'язані до певної реалізації цього інтерфейсу. Чи застосовується ця логіка до таких інтерфейсів, як java.util.Collection ?

Я б швидше побачив щось таке:

List<Foo> getFoos()

або

Set<Foo> getFoos()

замість

Collection<Foo> getFoos()

В останньому випадку я не знаю, з яким набором даних я маю справу, тоді як у перших двох випадках я можу зробити деякі припущення щодо замовлення та унікальності. Чи корисність java.util.Collection не є логічним батьком як для наборів, так і для списків?

Якщо ви зіткнулися з кодом, який використовував колекцію під час огляду коду, як би ви визначили, чи є виправданим його використання та які пропозиції ви б запропонували щодо його заміни більш конкретним інтерфейсом?


3
Чи траплялося вам помітити в java.security.cert, що це один тип повернення Collection<List<?>>? Поговоріть про кодування жаху!
Macneil

1
@Macneil Я не знаю, до якого класу ви звертаєтесь, але такий тип повернення справді може бути досить розумним. Це по суті говорить вам, що у вас є колекція (тобто містить купу речей, які не мають розумного впорядкування) списків (тобто містять речі, які мають розумне впорядкування) об'єктів (тобто предметів, типи яких ми не знаємо статистично для з будь-якої причини). Мені це не здається нерозумним.
Zero3

Відповіді:


13

Абстракції живуть довше, ніж реалізації

Загалом, чим абстрактніший ваш дизайн, тим довше він буде корисним. Отже, оскільки колекція більш абстрактна, що це підінтерфейси, то дизайн API, заснований на Collection, швидше залишатиметься корисним, ніж той, який базується на List.

Однак загальним принципом є використання найбільш підходящої абстракції . Отже, якщо ваша колекція повинна підтримувати впорядковані елементи, тоді доручайте список, якщо дублікатів не повинно бути, то мандат набір і так далі.

Примітка про загальний дизайн інтерфейсу

Оскільки вам цікаво використовувати інтерфейс колекції з дженериками, вам можуть допомогти наступні. Ефективна Java Джошуа Блох рекомендує наступний підхід при розробці інтерфейсу, який буде покладатися на дженерики: Producers Extend, Consumers Super

Це також відоме як правило PECS . По суті, якщо загальні колекції, що створюють дані, передаються вашому класу, підпис повинен виглядати так:

public void pushAll(Collection<? extends E> producerCollection) {}

Таким чином, вхідним типом може бути E або будь-який підклас E (E визначається як супер- і підклас себе в мові Java).

І навпаки, загальна колекція, яка передається для споживання даних, повинна мати такий підпис:

public void popAll(Collection<? super E> consumerCollection) {}

Метод буде правильно мати справу з будь-яким суперкласу Е. В цілому, використовуючи цей підхід зробить ваш інтерфейс менш дивним для користувачів , тому що ви будете в змозі передати Collection<Number>і Collection<Integer>і мати їх правильно лікувати.


6

CollectionІнтерфейс, і найбільш роздільна форма Collection<?>, відмінно підходить для параметрів , які ви приймаєте. На основі використання в самій бібліотеці Java він є більш поширеним як тип параметра, ніж тип повернення.

Що стосується типів повернення, я вважаю, що ваш пункт дійсний: Якщо від людей очікують доступ до нього, вони повинні знати порядок (у значенні Big-O) операції, що виконується. Я б переглянув Collectionповернений і додав його до іншої колекції, але, здається, трохи божевільним буде зателефонувати containsна нього, не знаючи, чи це операція O (1), O (log n) чи O (n). Звичайно, тільки тому, що у вас немає, це Setне означає, що це хеш-пакет або відсортований набір, але в якийсь момент ви зробите припущення, що інтерфейс був розумно реалізований (і тоді вам потрібно буде перейти до плану B, якщо ваше припущення показано неправильним).

Як згадує Том, іноді вам потрібно повернути а Collection, щоб зберегти інкапсуляцію. Ви не хочете, щоб деталі про впровадження просочилися, навіть якщо ви могли повернути щось більш конкретне. Або у випадку, про який згадував Том, ви можете повернути більш конкретний контейнер, але тоді вам доведеться його сконструювати.


2
Я думаю, що другий момент трохи слабкий. Ви не знаєте, як збиратиметься колекція незалежно від того, чи це колекція чи список - оскільки вони просто абстракції. Якщо у вас конкретний заключний клас, ви не можете сказати справді.
Марк Н

Крім того, якщо все, що ви знаєте, це те, що щось є колекцією, ви не маєте поняття, чи може вона містити дублікати чи ні. Якщо повернути це, один раз випадок, коли повернення колекції може бути доцільним, якщо у вас є колекція, яка не містить дублікатів і не має значного порядку (що, природно, було б набором), але де з певних вагомих причин реалізація методу повернення використовує Список. Ви не хочете повертати список, оскільки це означає значне замовлення, але ви не можете повернути набір, не пройшовши ригмарол виготовлення його. Отже, ви повертаєте колекцію.
Том Андерсон

@Tom: Добрий момент!
Macneil

5

Я поглянув би на це з цілком протилежної точки зору і запитав:

Якщо ви зіткнулися з кодом, який використовував Список <> під час огляду коду, як би ви визначили, чи виправдане його використання?

Виправдати це досить просто. Список ви використовуєте, коли вам потрібна певна функціональність, яку не пропонує колекція. Якщо вам не потрібна додаткова функціональність - яке виправдання маєте? (І я не купуватиму, "я вважаю за краще"

Є чимало випадків, коли ви будете використовувати колекцію для цілей лише для читання, заповнюючи її все одразу, повторюючи її цілком - чи вам коли-небудь потрібно індексувати річ вручну?

Навести реальний приклад. Скажімо, я виконую простий запит у базі даних. ( SELECT id,name,rep FROM people WHERE name LIKE '%squared') Я захоплюю відповідні дані, заповнюю об'єкти Person і вкладаю їх у PersonList)

  • Чи потрібно мені отримувати доступ за індексом? - Було б безглуздо. Немає відображення між індексом та ідентифікатором.
  • Чи потрібно вводити індекс? - Ні, СУБД вирішить, куди його поставити, якщо я взагалі додаю.

То яке виправдання я мав би для цих додаткових методів? (що в моєму PersonList так чи інакше залишатиметься без змін)


Справедлива точка. Я припускаю, що моє запитання стосується конкретного випадку, коли під час огляду коду я постійно бачу DAO, які повертають колекції, але я знаю, що ці виклики DAO завжди будуть повертатися наборами сутностей; моє заперечення полягає в тому, що в цих випадках тип повернення вказує на унікальність, і ця інформація корисна тому, хто має використовувати цей метод (наприклад, мені не потрібно перевіряти дублікати).
Філ

Якщо ви запитували БД, порівнюючи 2 результуючі об'єкти з рівними (), це взагалі не повинно створювати істину - тому вам знадобиться інший спосіб порівняння об'єктів для дублювання (наприклад, чи мають вони те саме ім’я, однаковий ідентифікатор, обидва?). Вам потрібно буде сказати своєму DAO, як порівнювати їх, якщо це потрібно для видалення дублікатів, але, оскільки ви користувач, який вирішує, чи існують дублікати - простіше просто зробити це зі збіркою з викличного коду. (Щоб уникнути більше шарів абстрагування, щоб повідомити DAO, як виконувати всі можливі перевірки рівності на планеті.)
Марк H

Погоджено, але ми використовуємо сплячий режим і гарантуємо, що наші суб'єкти впроваджують рівні (). Отже, коли DAO повертає сутності, ми можемо дуже швидко створити новий HashSet (). AddAll (результати) і повернути це назад до названого методу.
Філ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.