Я видел это упомянутое несколько раз, и мне не ясно, что это значит. Когда и зачем ты это делаешь?
Я знаю, что делают интерфейсы, но тот факт, что я не совсем уверен в этом, заставляет меня думать, что мне не хватает их правильного использования.
Это просто так, если вы должны были сделать:
IInterface classRef = new ObjectWhatever()
Вы могли бы использовать любой класс, который реализует IInterface
? Когда вам нужно это сделать? Единственное, о чем я могу думать, это если у вас есть метод, и вы не уверены, какой объект будет передан, за исключением его реализации IInterface
. Я не могу думать, как часто вам нужно будет это делать.
Кроме того, как вы могли бы написать метод, который принимает объект, который реализует интерфейс? Это возможно?
language-agnostic
oop
interface
Damien
источник
источник
Ответы:
Здесь есть несколько замечательных ответов на эти вопросы, которые подробно разбираются во всех подробностях об интерфейсах и слабой связи кода, инверсии управления и так далее. Есть некоторые довольно опрометчивые обсуждения, поэтому я хотел бы воспользоваться возможностью, чтобы немного разобраться, чтобы понять, почему интерфейс полезен.
Когда я впервые начал знакомиться с интерфейсами, я тоже был озадачен их актуальностью. Я не понимаю, зачем они тебе нужны. Если мы используем такой язык, как Java или C #, у нас уже есть наследование, и я рассматривал интерфейсы как более слабую форму наследования и думал: «Зачем беспокоиться?» В некотором смысле я был прав, вы можете рассматривать интерфейсы как своего рода слабую форму наследования, но помимо этого я наконец-то понял их использование в качестве языковой конструкции, рассматривая их как средство классификации общих черт или поведений, которые были продемонстрированы потенциально много не связанных классов объектов.
Например - скажем, у вас есть игра на SIM-карте и есть следующие классы:
Очевидно, что эти два объекта не имеют ничего общего с точки зрения прямого наследования. Но вы могли бы сказать, что они оба раздражают.
Допустим, в нашей игре должна быть какая-то случайная вещь, которая раздражает игрока, когда они обедают. Это может быть a
HouseFly
или aTelemarketer
или оба - но как вы допускаете оба с одной функцией? И как вы просите каждый другой тип объекта «делать их надоедливые вещи» таким же образом?Ключ понимать, что как
Telemarketer
иHouseFly
доля общей свободно интерпретировать поведение , даже если они не похожи с точки зрения моделирования их. Итак, давайте создадим интерфейс, который могут реализовать оба:Теперь у нас есть два класса, каждый из которых может раздражать по-своему. И они не должны происходить из одного и того же базового класса и иметь общие присущие им характеристики - они просто должны удовлетворять договору
IPest
- этот договор прост. Вы просто должныBeAnnoying
. В связи с этим мы можем смоделировать следующее:Здесь у нас есть столовая, которая принимает количество посетителей и количество вредителей - обратите внимание на использование интерфейса. Это означает, что в нашем маленьком мире членом
pests
массива может бытьTelemarketer
объект илиHouseFly
объект.ServeDinner
Метод вызывается , когда ужин и наши люди в столовой должны есть. В нашей маленькой игре именно тогда наши вредители выполняют свою работу - каждый вредитель должен быть раздраженIPest
интерфейсом. Таким образом, мы можем легко иметь и то,Telemarketers
и другое иHouseFlys
быть раздражающими по-своему - нас волнует только то, что у нас есть что-то вDiningRoom
объекте, который является вредным организмом, нам на самом деле все равно, что это такое, и они не могут иметь ничего общего с другим.Этот очень надуманный пример псевдокода (который тянулся намного дольше, чем я ожидал) просто предназначен для того, чтобы проиллюстрировать те вещи, которые наконец-то зажгли меня в плане того, когда мы можем использовать интерфейс. Я заранее прошу прощения за глупость примера, но надеюсь, что это поможет в вашем понимании. И, конечно же, другие опубликованные ответы, которые вы получили здесь, действительно охватывают весь спектр использования интерфейсов сегодня в шаблонах проектирования и методологиях разработки.
источник
BeAnnoying
как no-op; этот интерфейс может существовать вместо или в дополнение к интерфейсу для вещей, которые раздражают (если оба интерфейса существуют, интерфейс «вещи, которые раздражают» должен наследоваться от интерфейса «вещи, которые могут раздражать»). Недостаток использования таких интерфейсов состоит в том, что реализации могут быть обременены реализацией «раздражающего» числа методов-заглушек. Преимущество в том, что ...IPest[]
ссылках IPest, вы можете вызывать,BeAnnoying()
потому что у них есть этот метод, в то время как вы не можете вызывать другие методы без приведения. Однако для каждого объектаBeAnnoying()
будет вызван отдельный метод.Конкретный пример, который я привел студентам, состоит в том, что они должны написать
вместо
Они выглядят точно так же в короткой программе, но если вы продолжите использовать
myList
в своей программе 100 раз, вы можете начать видеть разницу. Первое объявление гарантирует, что вы вызываете только те методыmyList
, которые определеныList
интерфейсом (поэтому никакихArrayList
конкретных методов). Если вы запрограммировали интерфейс таким образом, позже вы можете решить, что вам действительно нужнои вам нужно всего лишь изменить свой код в этом месте. Вы уже знаете, что остальная часть вашего кода не делает ничего, что может быть нарушено при изменении реализации, потому что вы запрограммировали интерфейс .
Преимущества становятся еще более очевидными (я думаю), когда вы говорите о параметрах метода и возвращаемых значениях. Возьмите это к примеру:
Это объявление метода связывает вас с двумя конкретными реализациями (
ArrayList
иHashMap
). Как только этот метод вызывается из другого кода, любые изменения этих типов, вероятно, означают, что вам также придется изменить вызывающий код. Было бы лучше программировать на интерфейсы.Теперь не имеет значения, какой
List
вы возвращаете или какой типMap
передается в качестве параметра. Изменения, которые вы делаете внутриdoSomething
метода, не заставят вас изменить вызывающий код.источник
Программирование интерфейса говорит: «Мне нужна эта функциональность, и мне все равно, откуда она».
Рассмотрим (в Java)
List
интерфейсArrayList
иLinkedList
конкретные классы. Если все, что меня волнует, это то, что у меня есть структура данных, содержащая несколько элементов данных, к которым я должен обращаться через итерацию, я бы выбралList
(и это 99% времени). Если я знаю, что мне нужно постоянное время вставки / удаления из любого конца списка, я мог бы выбратьLinkedList
конкретную реализацию (или, более вероятно, использовать интерфейс очереди ). Если я знаю, что мне нужен произвольный доступ по индексу, я бы выбралArrayList
конкретный класс.источник
Использование интерфейсов является ключевым фактором в том, чтобы сделать ваш код легко тестируемым в дополнение к удалению ненужных связей между вашими классами. Создавая интерфейс, который определяет операции над вашим классом, вы позволяете классам, которые хотят использовать эту функциональность, возможность использовать его без прямой зависимости от вашего реализующего класса. Если позже вы решите изменить и использовать другую реализацию, вам нужно будет изменить только ту часть кода, в которой реализована реализация. Остальная часть кода не должна изменяться, потому что это зависит от интерфейса, а не от класса реализации.
Это очень полезно при создании модульных тестов. В тестируемом классе вы зависите от интерфейса и внедряете экземпляр интерфейса в класс (или фабрику, которая позволяет ему создавать экземпляры интерфейса по мере необходимости) через конструктор или свойство settor. Класс использует предоставленный (или созданный) интерфейс в своих методах. Когда вы собираетесь писать тесты, вы можете смоделировать или подделать интерфейс и предоставить интерфейс, который отвечает данными, настроенными в вашем модульном тесте. Вы можете сделать это, потому что ваш тестируемый класс имеет дело только с интерфейсом, а не с конкретной реализацией. Подойдет любой класс, реализующий интерфейс, включая ваш фиктивный или поддельный класс.
РЕДАКТИРОВАТЬ: Ниже приведена ссылка на статью, где Эрих Гамма обсуждает свою цитату «Программа для интерфейса, а не реализации».
http://www.artima.com/lejava/articles/designprinciples.html
источник
Программирование на интерфейсе не имеет абсолютно никакого отношения к абстрактным интерфейсам, как мы видим в Java или .NET. Это даже не концепция ООП.
Это означает, что вы не должны возиться с внутренностями объекта или структуры данных. Используйте абстрактный программный интерфейс или API для взаимодействия с вашими данными. В Java или C # это означает использование открытых свойств и методов вместо необработанного доступа к полям. Для C это означает использование функций вместо необработанных указателей.
РЕДАКТИРОВАТЬ: И с базами данных это означает использование представлений и хранимых процедур вместо прямого доступа к таблице.
источник
Вы должны посмотреть на инверсию контроля:
В таком случае вы бы не написали так:
Вы бы написали что-то вроде этого:
Это будет входить в основанную на правилах настройку
container
объекта и создавать для вас реальный объект, который может быть ObjectWhh независимо. Важно то, что вы могли бы заменить это правило чем-то, что использовало бы совершенно другой тип объекта, и ваш код все еще работал бы.Если мы оставим IoC вне таблицы, вы можете написать код, который знает, что он может общаться с объектом, который делает что-то конкретное , но не с тем, какой тип объекта или как он это делает.
Это пригодится при передаче параметров.
Что касается вашего вопроса в скобках «Кроме того, как вы могли бы написать метод, который принимает объект, который реализует интерфейс? Это возможно?», В C # вы просто использовали бы тип интерфейса для типа параметра, например так:
Это включается прямо в «разговор с объектом, который делает что-то конкретное». Описанный выше метод знает, чего ожидать от объекта, что он реализует все в IInterface, но ему все равно, какой это тип объекта, только то, что он придерживается контракта, а именно интерфейс.
Например, вы, вероятно, знакомы с калькуляторами и, возможно, использовали в свое время немало, но в большинстве случаев они все разные. С другой стороны, вы знаете, как должен работать стандартный калькулятор, поэтому вы можете использовать их все, даже если вы не можете использовать специфические функции каждого калькулятора, которых нет ни у одного другого.
Это красота интерфейсов. Вы можете написать кусок кода, который знает, что ему будут переданы объекты, от которых он может ожидать определенного поведения. Не важно, что это за объект, только то, что он поддерживает необходимое поведение.
Позвольте мне привести вам конкретный пример.
У нас есть специальная система перевода для оконных форм. Эта система перебирает элементы управления в форме и переводит текст в каждом. Система знает, как обрабатывать базовые элементы управления, такие как свойство «тип элемента управления, который имеет текст», и аналогичные базовые элементы, но для чего-либо базового она не дотягивает.
Теперь, поскольку элементы управления наследуются от предопределенных классов, которые мы не можем контролировать, мы можем сделать одну из трех вещей:
Итак, мы сделали Nr. 3. Все наши элементы управления реализуют ILocalizable, который представляет собой интерфейс, который дает нам один метод, возможность переводить «себя» в контейнер текста / правил перевода. Таким образом, форме не нужно знать, какой тип элемента управления она нашла, только что она реализует определенный интерфейс и знает, что существует метод, который можно вызвать для локализации элемента управления.
источник
Код для интерфейса Не реализация не имеет ничего общего с Java, как и с интерфейсом конструкции.
Эта концепция была широко известна в книгах «Паттерны / Банды четырех», но, скорее всего, была задолго до этого. Эта концепция, безусловно, существовала задолго до появления Java.
Конструкция Java Interface была создана, чтобы помочь в этой идее (среди прочего), и люди стали слишком сосредоточены на конструкции как центре значения, а не первоначальном намерении. Однако по этой причине у нас есть открытые и закрытые методы и атрибуты в Java, C ++, C # и т. Д.
Это означает просто взаимодействовать с открытым интерфейсом объекта или системы. Не беспокойтесь и даже не ожидайте, что он сделает то, что делает внутри. Не беспокойтесь о том, как это реализовано. Именно поэтому в объектно-ориентированном коде у нас есть открытые и частные методы / атрибуты. Мы намерены использовать открытые методы, потому что закрытые методы существуют только для внутреннего использования в классе. Они составляют реализацию класса и могут быть изменены по мере необходимости без изменения открытого интерфейса. Предположим, что в отношении функциональности метод класса будет выполнять одну и ту же операцию с тем же ожидаемым результатом каждый раз, когда вы вызываете его с одинаковыми параметрами. Это позволяет автору изменять работу класса, его реализацию, не нарушая взаимодействия людей с ним.
И вы можете программировать для интерфейса, а не для реализации, даже не используя конструкцию интерфейса. Вы можете программировать на интерфейс, а не на реализацию в C ++, которая не имеет конструкции Interface. Вы можете интегрировать две массивные корпоративные системы гораздо надежнее, если они взаимодействуют через открытые интерфейсы (контракты), а не вызывают методы для объектов, внутренних по отношению к системам. Ожидается, что интерфейсы всегда будут реагировать одинаково ожидаемым образом при одинаковых входных параметрах; если реализовано для интерфейса, а не для реализации. Концепция работает во многих местах.
Встряхните мысль о том, что интерфейсы Java как-то связаны с понятием «Программа для интерфейса, а не реализация». Они могут помочь применить концепцию, но они не являются концепцией.
источник
Похоже, вы понимаете, как работают интерфейсы, но не знаете, когда их использовать и какие преимущества они предлагают. Вот несколько примеров, когда интерфейс имеет смысл:
тогда я мог бы создать GoogleSearchProvider, YahooSearchProvider, LiveSearchProvider и т. д.
затем создайте JpegImageLoader, GifImageLoader, PngImageLoader и т. д.
Большинство надстроек и плагинов работают от интерфейсов.
Другое популярное использование - шаблон Repository. Скажем, я хочу загрузить список почтовых индексов из разных источников
затем я мог бы создать XMLZipCodeRepository, SQLZipCodeRepository, CSVZipCodeRepository и т. д. Для своих веб-приложений я часто создаю XML-репозитории на ранней стадии, чтобы я мог что-то запустить и запустить до того, как база данных SQL будет готова. Когда база данных готова, я пишу SQLRepository, чтобы заменить версию XML. Остальная часть моего кода остается неизменной, поскольку он работает исключительно на интерфейсах.
Методы могут принимать интерфейсы, такие как:
источник
Это делает ваш код намного более расширяемым и простым в обслуживании, если у вас есть наборы похожих классов. Я младший программист, поэтому я не эксперт, но я только что закончил проект, который требовал чего-то подобного.
Я работаю над программным обеспечением на стороне клиента, которое взаимодействует с сервером, на котором работает медицинское устройство. Мы разрабатываем новую версию этого устройства, в которой есть некоторые новые компоненты, которые клиент должен настраивать время от времени. Есть два типа новых компонентов, и они разные, но они также очень похожи. По сути, мне пришлось создать две формы конфигурации, два списка списков, два из всего.
Я решил, что было бы лучше создать абстрактный базовый класс для каждого типа элемента управления, который будет содержать почти всю реальную логику, а затем производные типы, чтобы позаботиться о различиях между этими двумя компонентами. Однако базовые классы не смогли бы выполнять операции с этими компонентами, если бы мне приходилось все время беспокоиться о типах (ну, они могли бы иметь место, но в каждом методе был бы оператор «if» или переключатель) ,
Я определил простой интерфейс для этих компонентов, и все базовые классы общаются с этим интерфейсом. Теперь, когда я что-то изменяю, это почти везде работает, и у меня нет дублирования кода.
источник
Там много объяснений, но сделать это еще проще. Возьмите для примера
List
. Можно реализовать список с помощью:Создавая интерфейс, скажем
List
. Вы только код для определения списка или чтоList
означает в реальности.Вы можете использовать любой тип внутренней реализации, скажем,
array
реализацию. Но предположим, что вы хотите изменить реализацию по какой-то причине, например, из-за ошибки или производительности. Тогда вам просто нужно изменить объявлениеList<String> ls = new ArrayList<String>()
наList<String> ls = new LinkedList<String>()
.Нигде в коде вам не придется ничего менять; Потому что все остальное было построено на определении
List
.источник
Если вы программируете на Java, хорошим примером является JDBC. JDBC определяет набор интерфейсов, но ничего не говорит о реализации. Ваши приложения могут быть написаны для этого набора интерфейсов. Теоретически, вы выбираете драйвер JDBC, и ваше приложение будет работать. Если вы обнаружите, что есть более быстрый или «лучший» или более дешевый драйвер JDBC или по какой-либо причине, вы можете теоретически заново настроить файл свойств, и без внесения каких-либо изменений в ваше приложение, оно все равно будет работать.
источник
Программирование на интерфейсах - это круто, это способствует слабой связи. Как упомянул @lassevk, Inversion of Control отлично подходит для этого.
Кроме того, посмотрите на твердые принципы . вот видео серия
Он проходит через жестко закодированный (сильно связанный пример), затем просматривает интерфейсы и, наконец, переходит к инструменту IoC / DI (NInject).
источник
Я опоздал на этот вопрос, но хочу упомянуть, что строка «Программа для интерфейса, а не для реализации» имела хорошее обсуждение в книге «Шаблоны проектирования GoF (Gang of Four)».
Указано на с. 18:
и, кроме того, это началось с:
Другими словами, не пишите это своим классам, чтобы у них был
quack()
метод для уток, а затемbark()
метод для собак, потому что они слишком специфичны для конкретной реализации класса (или подкласса). Вместо этого напишите метод, используя имена, которые являются достаточно общими для использования в базовом классе, такие какgiveSound()
илиmove()
, чтобы их можно было использовать для уток, собак или даже автомобилей, и тогда клиент ваших классов может просто сказать,.giveSound()
а не думая о том, использоватьquack()
лиbark()
или даже определить тип, прежде чем выдавать правильное сообщение для отправки объекту.источник
В дополнение к уже выбранному ответу (и различным информативным сообщениям здесь), я настоятельно рекомендую взять копию Head First Design Patterns . Это очень легко прочитать, и он ответит на ваш вопрос напрямую, объяснит, почему это важно, и покажет вам много шаблонов программирования, которые вы можете использовать, чтобы использовать этот принцип (и другие).
источник
Чтобы добавить к существующим сообщениям, иногда кодирование интерфейсов помогает в больших проектах, когда разработчики работают над отдельными компонентами одновременно. Все, что вам нужно, это определить интерфейсы заранее и написать код для них, в то время как другие разработчики пишут код для интерфейса, который вы реализуете.
источник
Это также хорошо для модульного тестирования, вы можете внедрить свои собственные классы (которые отвечают требованиям интерфейса) в класс, который зависит от него
источник
Может быть полезно программировать на интерфейсы, даже если мы не зависим от абстракций.
Программирование на интерфейсах заставляет нас использовать контекстуально подходящее подмножество объекта . Это помогает, потому что это:
Например, рассмотрим
Person
класс , который реализуетFriend
иEmployee
интерфейс.В контексте дня рождения человека мы программируем
Friend
интерфейс, чтобы предотвратить обращение с человеком как сEmployee
.В контексте работы человека мы программируем
Employee
интерфейс, чтобы предотвратить размывание границ рабочего места.Отлично. Мы вели себя соответствующим образом в разных контекстах, и наше программное обеспечение работает хорошо.
В далеком будущем, если наш бизнес переходит на работу с собаками, мы можем довольно легко изменить программное обеспечение. Сначала мы создаем
Dog
класс, который реализует обаFriend
иEmployee
. Тогда мы благополучно изменимnew Person()
наnew Dog()
. Даже если обе функции имеют тысячи строк кода, это простое редактирование будет работать, потому что мы знаем, что верно следующее:party
использует толькоFriend
подмножествоPerson
.workplace
использует толькоEmployee
подмножествоPerson
.Dog
реализует какFriend
иEmployee
интерфейсы.С другой стороны, если бы один из них
party
илиworkplace
был запрограммирован противPerson
, был бы риск того, что у обоих будетPerson
специфичный код. Переход отPerson
кDog
потребовал бы, чтобы мы прочесали код, чтобы уничтожить любойPerson
специфический код,Dog
который не поддерживает.Мораль : программирование интерфейсов помогает нашему коду вести себя надлежащим образом и быть готовым к изменениям. Это также подготавливает наш код к зависимости от абстракций, что дает еще больше преимуществ.
источник
Если я пишу новый класс
Swimmer
для добавления функциональностиswim()
и нужно использовать объект класса скажемDog
, и этотDog
класс реализует интерфейс,Animal
который объявляетswim()
.В верхней части иерархии (
Animal
) она очень абстрактная, а в нижней части (Dog
) она очень конкретная. То, как я думаю о «программировании для интерфейсов», заключается в том, что, когда я пишуSwimmer
класс, я хочу написать свой код в соответствии с интерфейсом, который находится настолько далеко от той иерархии, которая в данном случае являетсяAnimal
объектом. Интерфейс свободен от деталей реализации и, следовательно, делает ваш код слабосвязанным.Детали реализации могут изменяться со временем, однако это не повлияет на оставшийся код, поскольку все, с чем вы взаимодействуете, - это интерфейс, а не реализация. Вам все равно, на что похожа реализация ... все, что вы знаете, это то, что будет класс, который будет реализовывать интерфейс.
источник
Итак, просто чтобы понять это правильно, преимущество интерфейса состоит в том, что я могу отделить вызов метода от любого конкретного класса. Вместо этого создаю экземпляр интерфейса, где реализация дается от того класса, который я выберу, который реализует этот интерфейс. Таким образом, позволяя мне иметь много классов, которые имеют схожие, но немного отличающиеся функциональные возможности, а в некоторых случаях (в случаях, связанных с намерением интерфейса) не важно, какой это объект.
Например, я мог бы иметь интерфейс движения. Метод, который заставляет что-то «двигаться», и любой объект (Person, Car, Cat), который реализует интерфейс движения, может быть передан и задан для перемещения. Без метода каждый знает тип класса.
источник
Представьте, что у вас есть продукт под названием «Зебра», который можно расширить с помощью плагинов. Он находит плагины путем поиска DLL в некотором каталоге. Он загружает все эти библиотеки DLL и использует отражение, чтобы найти любые классы, которые реализуют
IZebraPlugin
, а затем вызывает методы этого интерфейса для связи с плагинами.Это делает его полностью независимым от какого-либо конкретного класса плагина - его не волнует, что это за классы. Нужно только, чтобы они соответствовали спецификации интерфейса.
Интерфейсы - это способ определения точек расширяемости, подобных этому. Код, который взаимодействует с интерфейсом, более слабо связан - фактически он вообще не связан ни с каким другим конкретным кодом. Он может взаимодействовать с плагинами, написанными годами позже людьми, которые никогда не встречались с первоначальным разработчиком.
Вместо этого вы можете использовать базовый класс с виртуальными функциями - все плагины будут производными от базового класса. Но это намного более ограничивает, потому что класс может иметь только один базовый класс, тогда как он может реализовывать любое количество интерфейсов.
источник
C ++ объяснение.
Думайте об интерфейсе как о ваших открытых методах классов.
Затем вы можете создать шаблон, который «зависит» от этих открытых методов, чтобы выполнить свою собственную функцию (она делает вызовы функций, определенные в общедоступном интерфейсе классов). Допустим, этот шаблон является контейнером, как класс Vector, а интерфейс, от которого он зависит, является алгоритмом поиска.
Любой класс алгоритма, который определяет функции / интерфейс, к которым Vector обращается, будет удовлетворять «контракту» (как кто-то объяснил в исходном ответе). Алгоритмы даже не должны быть одного и того же базового класса; единственное требование - чтобы в вашем алгоритме были определены функции / методы, от которых зависит Вектор (интерфейс).
Смысл всего этого в том, что вы можете предоставить любой другой алгоритм / класс поиска, если он предоставляет интерфейс, от которого зависит Vector (поиск по пузырькам, последовательный поиск, быстрый поиск).
Возможно, вы также захотите спроектировать другие контейнеры (списки, очереди), которые будут использовать тот же алгоритм поиска, что и Vector, если они будут выполнять интерфейс / контракт, от которого зависят ваши алгоритмы поиска.
Это экономит время (принцип ООП «повторное использование кода»), поскольку вы можете написать алгоритм один раз, а не снова, снова и снова, специфично для каждого нового объекта, который вы создаете, без чрезмерного усложнения проблемы с заросшим деревом наследования.
Что касается «упущения» в отношении того, как все работает; большой (по крайней мере, в C ++), поскольку именно так работает большая часть стандартной библиотеки TEMPLATE.
Конечно, при использовании наследственных и абстрактных классов методология программирования интерфейса меняется; но принцип тот же, ваши публичные функции / методы - это интерфейс ваших классов.
Это огромная тема и один из основополагающих принципов проектирования шаблонов.
источник
В Java все эти конкретные классы реализуют интерфейс CharSequence:
Эти конкретные классы не имеют общего родительского класса, кроме Object, поэтому ничто не связывает их, кроме того факта, что каждый из них имеет какое-то отношение к массивам символов, представляя их или манипулируя ими. Например, символы String нельзя изменить после создания экземпляра объекта String, тогда как символы StringBuffer или StringBuilder можно редактировать.
Тем не менее, каждый из этих классов способен соответствующим образом реализовать методы интерфейса CharSequence:
В некоторых случаях классы библиотеки классов Java, которые раньше принимали String, были пересмотрены, чтобы теперь принимать интерфейс CharSequence. Поэтому, если у вас есть экземпляр StringBuilder, вместо извлечения объекта String (что означает создание экземпляра нового объекта), он может вместо этого просто передать сам StringBuilder, поскольку он реализует интерфейс CharSequence.
Добавляемый интерфейс, который реализуют некоторые классы, имеет почти одинаковые преимущества для любой ситуации, когда символы могут добавляться к экземпляру базового экземпляра объекта класса. Все эти конкретные классы реализуют интерфейс Applicable:
источник
CharSequence
анемичные. Я хотел бы, чтобы Java и .NET позволили интерфейсам иметь реализацию по умолчанию, чтобы люди не урезали интерфейсы исключительно для минимизации стандартного кода. При любой допустимойCharSequence
реализации можно эмулировать большинство функций,String
используя только четыре вышеуказанных метода, но многие реализации могут выполнять эти функции гораздо более эффективно другими способами. К сожалению, даже если конкретная реализацияCharSequence
хранит все в одномchar[]
и может выполнять много ...indexOf
быстро, нет способа, чтобы вызывающий, не знакомый с конкретной реализацией,CharSequence
мог попросить его сделать это, вместо того, чтобы использовать егоcharAt
для изучения каждого отдельного символа.Короткая история: Почтальона просят пойти домой после дома и получить обложки (письма, документы, чеки, подарочные карты, заявление, любовное письмо) с адресом, написанным на нем для доставки.
Предположим, что нет прикрытия и попросите почтальона пойти домой после дома и получить все вещи и доставить другим людям, почтальон может запутаться.
Так что лучше оберните его крышкой (в нашей истории это интерфейс), тогда он отлично справится со своей работой.
Теперь работа почтальона - получать и доставлять только обложки (он не стал бы беспокоиться о том, что находится внутри обложки).
Создать тип
interface
не фактического типа, но реализуйте его с реальным типом.Создание интерфейса означает, что ваши компоненты легко вписываются в остальную часть кода.
Я приведу вам пример.
у вас есть интерфейс AirPlane, как показано ниже.
Предположим, у вас есть методы в вашем классе контроллеров плоскостей, такие как
а также
реализовано в вашей программе. Он не будет сломает ваш код. Я имею в виду, что это не должно измениться, если он принимает аргументы как
AirPlane
.Потому что он будет принимать любой самолет, несмотря на фактический тип,
flyer
,highflyr
,fighter
и т.д.Также в коллекции:
List<Airplane> plane;
// Приниму все ваши самолеты.Следующий пример прояснит ваше понимание.
У вас есть истребитель, который реализует его, так что
То же самое для HighFlyer и других предметов:
Теперь думайте, что ваши классы контроллера используют
AirPlane
несколько раз,Предположим, что ваш класс Controller - это ControlPlane, как показано ниже,
Сюда приходит магия, поскольку вы можете создавать новые
AirPlane
экземпляры типов столько раз, сколько хотите, и вы не меняете кодControlPlane
класса.Вы можете добавить экземпляр ...
Вы также можете удалить экземпляры ранее созданных типов.
источник
Интерфейс похож на контракт, где вы хотите, чтобы ваш класс реализации реализовывал методы, записанные в контракте (интерфейсе). Поскольку Java не обеспечивает множественное наследование, «программирование для интерфейса» является хорошим способом достижения множественного наследования.
Если у вас есть класс A, который уже расширяет какой-то другой класс B, но вы хотите, чтобы этот класс A также следовал определенным рекомендациям или реализовывал определенный контракт, то вы можете сделать это с помощью стратегии «программирования для взаимодействия».
источник
Примечание. Мы не смогли создать экземпляр интерфейса, не реализованного классом - True.
Примечание: теперь мы можем понять, что произошло, если Bclass и Cclass реализовали один и тот же Dintf.
Что мы имеем: Те же прототипы интерфейса (имена функций в интерфейсе) и вызов различных реализаций.
Библиография: Прототипы - Википедия
источник
Программа для интерфейса позволяет легко изменять реализацию контракта, определенного интерфейсом. Это позволяет слабую связь между контрактом и конкретными реализациями.
Посмотрите на этот вопрос SE для хорошего примера.
Почему интерфейс для Java-класса должен быть предпочтительным?
Да. Это будет иметь незначительное снижение производительности в течение нескольких секунд. Но если ваше приложение требует динамического изменения реализации интерфейса, не беспокойтесь о влиянии на производительность.
Не пытайтесь избегать множественных реализаций интерфейса, если они нужны вашему приложению. В отсутствие тесной связи интерфейса с одной конкретной реализацией вам, возможно, придется развернуть исправление, чтобы изменить одну реализацию на другую реализацию.
Один хороший пример использования: реализация шаблона стратегии:
Пример шаблона стратегии в реальном мире
источник
Программа для интерфейса - это термин из книги GOF. Я бы не сказал, что это связано с интерфейсом Java, а скорее с реальными интерфейсами. чтобы добиться чистого разделения слоев, вам нужно создать некоторое разделение между системами, например: допустим, у вас есть конкретная база данных, которую вы хотите использовать, вы никогда не «программируете на базу данных», а «программируете на интерфейс хранилища». Точно так же вы никогда не «программируете на веб-сервис», а скорее программируете на «клиентский интерфейс». это так, что вы можете легко поменять вещи.
я считаю, что эти правила помогают мне:
1 . мы используем интерфейс Java, когда у нас есть несколько типов объектов. если у меня есть только один объект, я не вижу смысла. если есть хотя бы две конкретные реализации какой-то идеи, то я бы использовал интерфейс Java.
2 . если, как я уже говорил выше, вы хотите перенести разделение с внешней системой (системой хранения) на вашу собственную систему (локальную БД), то также используйте интерфейс.
обратите внимание, что есть два способа определить, когда их использовать. надеюсь это поможет.
источник
Также я вижу много хороших и объяснительных ответов здесь, поэтому я хочу высказать свою точку зрения здесь, включая некоторую дополнительную информацию, которую я заметил при использовании этого метода.
Модульное тестирование
Последние два года я писал хобби-проект и не писал для него модульные тесты. После того, как я написал около 50 тыс. Строк, я обнаружил, что действительно необходимо написать модульные тесты. Я не использовал интерфейсы (или очень экономно) ... и когда я сделал свой первый модульный тест, я обнаружил, что это сложно. Почему?
Потому что мне пришлось сделать много экземпляров классов, используемых для ввода в качестве переменных и / или параметров класса. Таким образом, тесты больше похожи на интеграционные тесты (создание полного «каркаса» классов, поскольку все они были связаны).
Боязнь интерфейсов Поэтому я решил использовать интерфейсы. Я боялся, что мне придется реализовывать всю функциональность везде (во всех используемых классах) несколько раз. В некотором смысле это так, однако, используя наследование, оно может быть значительно уменьшено.
Комбинация интерфейсов и наследования Я обнаружил, что комбинация очень хороша для использования. Я приведу очень простой пример.
Таким образом, копирование кода не является обязательным, но в то же время имеет преимущество использования автомобиля в качестве интерфейса (ICar).
источник
Давайте начнем с некоторых определений:
Интерфейс n. Набор всех сигнатур, определенных операциями объекта, называется интерфейсом к объекту.
Введите n. Определенный интерфейс
Простой пример интерфейса , как определено выше , будет все методы объекта PDO , такие как
query()
,commit()
,close()
т. Д., В целом, а не отдельно. Эти методы, то есть его интерфейс, определяют полный набор сообщений, запросов, которые могут быть отправлены объекту.Типа , как определено выше , представляет собой конкретный интерфейс. Я буду использовать выдуманный интерфейс формы для демонстрации:
draw()
,getArea()
, иgetPerimeter()
т.д ..Если объект имеет тип базы данных, мы имеем в виду, что он принимает сообщения / запросы интерфейса базы данных
query()
иcommit()
т. Д. Объекты могут быть разных типов. Вы можете иметь объект базы данных типа фигуры, если он реализует свой интерфейс, и в этом случае это будет подтип .Многие объекты могут иметь разные интерфейсы / типы и реализовывать этот интерфейс по-разному. Это позволяет нам заменять объекты, позволяя нам выбирать, какой из них использовать. Также известен как полиморфизм.
Клиент будет знать только интерфейс, а не реализацию.
Таким образом , в сущности программирования на интерфейс предполагает сделать некоторый тип абстрактного класса , такой как
Shape
с интерфейсом только указанный то естьdraw()
,getCoordinates()
, иgetArea()
т.д .. И тогда есть различные классы конкретные реализации этих интерфейсов , таких как класс Circle, Square класса Triangle класса. Следовательно, программа для интерфейса не является реализацией.источник
«Программа для интерфейса» означает, что вы не предоставите жесткий код правильно, то есть ваш код должен быть расширен, не нарушая предыдущую функциональность. Просто расширения, а не редактирование предыдущего кода.
источник