Чому слід визначати об’єкт Java за допомогою інтерфейсу (наприклад, Map), а не реалізації (HashMap)


17

У більшості кодів Java я бачу, як люди заявляють такі об'єкти Java:

Map<String, String> hashMap = new HashMap<>();
List<String> list = new ArrayList<>();

замість:

HashMap<String, String> hashMap = new HashMap<>();
ArrayList<String> list = new ArrayList<>();

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


Відповіді:


26

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

Слід сказати, що в цьому є винятки, коли впровадження актуально. Якщо вам потрібна карта , коли важливий порядок, то може вимагати TreeMapабо LinkedHashMapбути передані, або ще краще , SortedMapякий не визначає реалізацію конкретної. Це зобов'язує зухвалому обов'язково пройти певний тип реалізації карти і сильно натякає , що порядок є важливим. Але це може сказати, чи можете ви змінити SortedMapта передати несортовану? Так, звичайно, проте очікуйте, що в результаті відбудуться погані речі.

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


Коли вам потрібна сортована карта, тип параметра повинен бути SortedMap, а не TreeMap.
Головоногі

@Arian SortedMap- одна з декількох реалізацій, яка стосується замовлення. Це крім суть. TreeMapтакож впорядковує елементи відповідно до ключових програм Comparableабо Comparatorінтерфейсу.
Ніл

Ні, SortedMap не є реалізацією, саме в цьому справа. Це інтерфейс для карт, які сортуються за клавішами.
Головопад

1
@Arian Ah Я бачу, що ти маєш на увазі. Правда, краще SortedMap, оскільки це не змушує реалізовувати. Я внесу належні корективи.
Ніл

Насправді, це LinkedHashMapне реалізує SortedMap. Єдиними підкласами SortedMapє ConcurrentSkipListMapі TreeMap.
bcorso

10

Словами Лаймана:

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

Використовуючи натомість стандартні штекери, вони дозволяють підключити однакові програми до будь-якого сумісного штекера навколо будинку.

З точки зору настінного розетки, не має значення, підключаєте ви телевізор чи стерео.

Це робить і прилад, і розетку більш корисними.

Візьмемо для прикладу метод, який приймає Map як аргумент.

Метод буде працювати незалежно від того, чи передаєте ви йому HashMap або LinkedHashMap, якщо це підклас Map.

Це принцип заміни Ліскова .

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

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


4

Це слід дотримуватися принципу сегрегації інтерфейсу ("Я" в SOLID ). Це не дозволяє коду, який використовує ці об'єкти, залежно від методів тих об'єктів, які йому не потрібні, що робить код менш зв'язаним, а тому легше змінювати.

Наприклад, якщо пізніше ви дізнаєтесь, що вам справді потрібен а LinkedHashMap, ви можете сміливо внести ці зміни, не впливаючи на будь-який інший код.

Однак, тут є торгівля, оскільки ви штучно обмежуєте код, який може сприймати ваш об'єкт як параметр. Скажімо , є функція де - то , що вимагаєHashMap з якої - то причини. Якщо ви повернете a Map, ви не можете передати об'єкт у цю функцію. Ви повинні збалансувати вірогідність того, що в майбутньому вам потрібна додаткова функціональність, яка є в більш конкретному класі, з бажанням обмежити зв'язок і зберегти ваш публічний інтерфейс якомога меншим.


3

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

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.