В большинстве кодов 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-объект с использованием интерфейса, а не реализации, которая фактически будет использоваться?
Ответы:
Причина в том, что реализация этих интерфейсов, как правило, не имеет значения при их обработке, поэтому, если вы обязываете вызывающую сторону передавать a
HashMap
в метод, то вы по существу обязываете какую реализацию использовать. Таким образом, как правило, вы должны обрабатывать его интерфейс, а не фактическую реализацию, и избегать боли и страданий, которые могут привести к необходимости изменять все сигнатуры методов,HashMap
когда вы решите использовать ихLinkedHashMap
вместо этого.Следует сказать, что есть исключения из этого, когда реализация актуальна. Если вам нужна карта, когда важен порядок, вы можете потребовать, чтобы a
TreeMap
или aLinkedHashMap
были переданы, или, что еще лучше, вSortedMap
которых не указана конкретная реализация. Это обязывает вызывающему обязательно пройти определенный тип реализации карты и сильно намекает , что порядок является важным. Тем не менее, вы могли бы переопределитьSortedMap
и передать несортированный? Да, конечно, однако ожидайте, что плохие вещи произойдут в результате.Однако лучшая практика все еще диктует, что, если это не важно, вы не должны использовать конкретные реализации. Это правда в целом. Если вы имеете дело с
Dog
иCat
которые вытекают изAnimal
того, чтобы наилучшим образом использовать наследование, вы должны вообще избежать методов , специфичных дляDog
илиCat
. Скорее все методы вDog
илиCat
должны переопределять методы,Animal
и это избавит вас от проблем в долгосрочной перспективе.источник
SortedMap
, а неTreeMap
.SortedMap
- это одна из нескольких реализаций, которые занимаются упорядочением. Это помимо смысла.TreeMap
также упорядочивает элементы в соответствии с реализацией ключаComparable
или заданнымComparator
интерфейсом.LinkedHashMap
не реализуетSortedMap
. Единственными подклассамиSortedMap
являютсяConcurrentSkipListMap
иTreeMap
.По словам Леймана:
По той же причине производители электрических приборов строили свои изделия с помощью электрических розеток, а не просто выдернутых кабелей, а дома поставляются с розетками вместо отрывных кабелей, торчащих из стены.
Вместо этого, используя стандартные вилки, они позволяют подключать одинаковые приборы к любой совместимой вилке по всему дому.
С точки зрения настенной розетки не имеет значения, подключаете ли вы телевизор или стереосистему.
Это делает и прибор, и розетку более полезными.
Возьмем, к примеру, метод, который принимает карту в качестве аргумента.
Этот метод будет работать независимо от того, передаете ли вы ему HashMap или LinkedHashMap, если это подкласс Map.
Это принцип замещения Лискова .
В приведенном вами примере кода это означает, что вы можете по какой-то причине позже изменить конкретную реализацию Hash, и вам не нужно будет изменять остальную часть кода.
Проблема с программным обеспечением заключается в том, что, поскольку относительно легко изменить вещи позже, не тратя кирпичи или раствор, люди предполагают, что такие предварительные размышления того не стоят. Но реальность показала нам, что обслуживание программного обеспечения очень дорого.
источник
Это должно следовать принципу разделения интерфейса («Я» в ТВЕРДОМ ). Он предотвращает зависимость кода, использующего эти объекты, от методов тех объектов, которые ему не нужны, что делает код менее связанным и, следовательно, более легким для изменения.
Например, если вы узнаете позже, что вам действительно нужно
LinkedHashMap
, вы можете безопасно внести это изменение, не затрагивая любой другой код.Однако есть компромисс, потому что вы искусственно ограничиваете код, который может принимать ваш объект в качестве параметра. Скажем , есть функция где - то , что требует
HashMap
по какой - то причине. Если вы возвращаетеMap
, вы не можете передать свой объект в эту функцию. Вы должны уравновесить вероятность того, что когда-нибудь в будущем понадобится дополнительная функциональность, относящаяся к более конкретному классу, с желанием ограничить связывание и сделать ваш публичный интерфейс как можно меньшим.источник
Наличие переменной, привязанной к интерфейсу, гарантирует, что ни одно из применений этой переменной не будет использовать
HashMap
определенные функциональные возможности, которые могут не существовать в интерфейсе, поэтому экземпляр может быть изменен без беспокойства позднее на другую реализацию, если новый экземпляр также реализует интерфейс.По этой причине, всякий раз, когда вы хотите использовать интерфейс объектов, всегда рекомендуется объявлять ваши переменные как интерфейс, а не как конкретную реализацию, это относится ко всем типам объектов, которые вы можете использовать, которые имеют интерфейс. Причина, по которой вы часто это видите, заключается в том, что многие люди встраивают это в привычку.
Тем не менее, иногда небезопасно отказываться от использования интерфейсов, и большинство из нас небрежно не всегда следуют этому правилу без какого-либо реального вреда. Это просто хорошая практика придерживаться, когда у вас есть чувство, что код может быть изменен и нуждается в обслуживании / росте в будущем. Это меньше беспокоит, когда вы взламываете код, который, как вы думаете, не будет иметь долгую жизнь или имеет большое значение. Кроме того, нарушение этого правила обычно имеет небольшое следствие, что изменение реализации на другое может потребовать некоторого рефакторинга, поэтому, если вы не всегда будете следовать ему, вы не сильно пострадаете, хотя и следовать этому не будет никакого реального вреда. ,
источник