Кодирование интерфейсов?
Я хочу укрепить свое понимание концепции "кодирование для интерфейса". Насколько я понимаю, один создает интерфейсы для определения ожидаемой функциональности, а затем реализует эти "контракты" в конкретных классах. Чтобы использовать интерфейс, можно просто вызвать методы в экземпляре конкретного класса.
Очевидным преимуществом является знание функциональности, предоставляемой конкретным классом, независимо от его конкретной реализации.
Исходя из вышесказанного:
- Есть ли какие-либо заблуждения в моем понимании "кодирования для интерфейсов"?
- Есть ли какие-либо преимущества кодирования для интерфейсов, которые я пропустил?
Спасибо.
Ответы
Ответ 1
Только одна возможная коррекция:
Чтобы использовать интерфейс, можно просто вызвать методы в экземпляре конкретного класса.
Можно было бы вызвать методы ссылки на интерфейс типа, который используется для использования конкретного класса в качестве реализации:
List<String> l = new ArrayList<String>();
l.add("foo");
l.add("bar");
Если вы решили переключиться на другую реализацию List, код клиента работает без изменений:
List<String> l = new LinkedList<String>();
Это особенно полезно для скрытия деталей реализации, автоматического создания прокси и т.д.
Вы обнаружите, что такие фреймворки, как spring и guice поощрять программирование к интерфейсу, Это основа для таких идей, как aspect-oriented, автоматически созданные прокси для управления транзакциями и т.д.
Ответ 2
Ваше понимание кажется правильным. Ваш коллега просто качнулся у вашего стола и имел все последние фотографии рождественской вечеринки, в которой вы пьяным боссом играли на его пальцевом пальце. Ваш коллега, и вы не думаете дважды о том, как работает этот thumbdrive, для вас это черный ящик, но вы знаете, что он работает из-за интерфейса USB.
Неважно, будь то SanDisk или Titanium (даже не уверен, что это бренд), размер и цвет тоже не имеют значения. Фактически, единственное, что имеет значение, это то, что он не сломан (читается) и что он подключается к USB.
Ваш USB-накопитель соблюдает контракт, это, по сути, интерфейс. Можно предположить, что он выполняет некоторые основные обязанности:
- Вилки в USB
-
Вызывается методом контракта CopyDataTo:
public Интерфейс IUSB {
void CopyDataTo (строка somePath);//используется для копирования данных с миниатюрного диска на...
}
-
Удерживается по методу контракта CopyDataFrom:
public Интерфейс IUSB {
void CopyDataFrom();//используется для копирования данных с вашего ПК на миниатюрный диск
}
Возможно, эти методы не поддерживаются, но интерфейс IUSB - это просто контракт, которым должны следовать разработчики мини-дисков, чтобы обеспечить функциональность на разных платформах/поставщиках. Таким образом, SanDisk делает свой thumbdrive интерфейсом:
public class SanDiskUSB : IUSB
{
//todo: define methods of the interface here
}
Ари, я думаю, у вас уже есть четкое понимание (по тому, как это звучит) о том, как работают интерфейсы.
Ответ 3
Основное преимущество заключается в том, что использование интерфейса свободно сочетает класс с его зависимостями. Затем вы можете изменить класс или реализовать новую конкретную реализацию интерфейса без необходимости изменять классы, которые зависят от него.
Ответ 4
Чтобы использовать интерфейс, можно просто вызвать методы в экземпляре конкретного класса.
Обычно у вас будет переменная, типизированная для типа интерфейса, что позволяет получить доступ только к методам, определенным в интерфейсе.
Очевидным преимуществом является знание функциональности, предоставляемой конкретным классом, независимо от ее конкретной реализации.
Сорт. Самое главное, это позволяет писать API, которые берут параметры с типами интерфейсов. Пользователи API могут затем передавать свои собственные классы (которые реализуют эти интерфейсы), и ваш код будет работать на этих классах, даже если они еще не существовали, когда они были написаны (например, java.util.Arrays.sort() способный сортировать все, что реализует Comparable
, или поставляется с подходящим Comparator
).
С точки зрения дизайна интерфейсы позволяют/обеспечивают четкое разделение между контрактами API и деталями реализации.
Ответ 5
Цель кодирования против интерфейсов - отделить ваш код от конкретной используемой реализации. То есть ваш код не будет делать предположений о конкретном типе, только интерфейс. Следовательно, конкретная реализация может быть обменена без необходимости корректировки вашего кода.
Ответ 6
Вы не указали информацию о том, как вы получаете реализацию интерфейса, что важно. Если вы явно создаете экземпляр класса реализации с помощью конструктора, ваш код привязан к этой реализации. Вы можете использовать factory, чтобы получить экземпляр для вас, но затем вы привязаны к factory, как и раньше, к классу внедрения. Третья альтернатива заключается в использовании инъекции зависимостей, которая имеет factory подключает объект реализации к объекту, который его использует, и в этом случае вы избегаете наличия класса, который использует объект, привязанный к классу реализации, или к factory.
Ответ 7
Думаю, вы могли намекнуть на это, но я считаю, что одним из самых больших преимуществ кодирования интерфейса является то, что вы нарушаете зависимость от конкретной реализации. Вы можете добиться ослабления связи и упростить передачу конкретных реализаций без изменения кода. Если вы просто учитесь, я бы посмотрел на различные шаблоны проектирования и как они решают проблемы путем кодирования на интерфейсы. Чтение книги Head First: Design Patterns действительно помогло мне щелкнуть.
Ответ 8
Как я понимаю, один создает интерфейсы для определения ожидаемой функциональности, а затем реализует эти "контракты" в конкретных классах.
Единственная мутация, которую я вижу в вашем мышлении, такова: вы собираетесь вызывать ожидаемые контракты, а не ожидаемую функциональность. Функциональность реализована в конкретных классах. Интерфейс только утверждает, что вы сможете называть то, что реализует интерфейс с ожидаемыми сигнатурами методов. Функциональность скрыта от вызывающего объекта.
Это позволит вам растянуть свое мышление на полиморфизм следующим образом.
SoundMaker sm = new Duck();<br/>
SoundMaker sm1 = new ThunderousCloud();
sm.makeSound(); // quack, calls all sorts of stuff like larynx, etc.<br/>
sm1.makeSound(); // BOOM!, completely different operations here...