Зачем использовать интерфейсы, множественное наследование и интерфейсы, преимущества интерфейсов?
У меня все еще есть путаница в этом. То, что я нашел до сих пор,
(Подобные вопросы уже заданы здесь, но у меня были другие моменты.)
-
Интерфейс - это коллекция ТОЛЬКО абстрактных методов и конечных полей.
-
В Java нет множественного наследования.
-
Интерфейсы могут использоваться для достижения множественного наследования в Java.
-
Одна сильная сторона наследования заключается в том, что мы можем использовать код базового класса в производном классе, не записывая его снова. Возможно, это самая важная вещь для наследования.
Теперь..
Q1. Поскольку интерфейсы имеют только абстрактные методы (без кода), так как мы можем сказать, что если мы реализуем какой-либо интерфейс, то это наследование? Мы не используем его код.
Q2. Если реализация интерфейса не является наследованием, то как интерфейсы используются для достижения множественного наследования?
Q3. Во всяком случае, в чем преимущество использования интерфейсов? У них нет никакого кода. Нам нужно писать код снова и снова во всех классах, которые мы реализуем.
Тогда зачем создавать интерфейсы?
ПРИМЕЧАНИЕ. Я нашел один случай, в котором интерфейсы полезны. Один из примеров этого - как в интерфейсе Runnable, у нас есть открытый метод void run(), в котором мы определяем функциональность потока, и есть встроенная кодировка, что этот метод будет выполняться как отдельный поток. Поэтому нам просто нужно закодировать, что делать в потоке, Rest предварительно задан. Но эта вещь также может быть достигнута с использованием абстрактных классов и всего.
Тогда каковы преимущества использования интерфейсов? Это действительно многократное наследование, которое мы достигаем с помощью интерфейсов?
Ответы
Ответ 1
Интерфейсы - это набор конечных статических полей и абстрактных методов (недавно в Java 8 добавлена поддержка наличия статических методов в интерфейсе).
Интерфейсы создаются в ситуациях, когда мы знаем, что какая-то задача должна быть выполнена, но то, как она должна быть выполнена, может отличаться. Другими словами, мы можем сказать, что реализуем интерфейсы, чтобы наш класс начал вести себя определенным образом.
Позвольте мне объяснить на примере, мы все знаем, что такое животные. Как Лев - животное, обезьяна - животное, слон - животное, корова - животное и так далее. Теперь мы знаем, что все животные что-то едят и спят. Но способ, которым каждое животное может что-то есть или спать, может отличаться. Как Лев ест, охотясь на других животных, где корова ест траву. Но оба едят. Таким образом, мы можем иметь некоторый псевдокод, как это,
interface Animal {
public void eat();
public void sleep();
}
class Lion implements Animal {
public void eat() {
// Lion way to eat
}
public void sleep(){
// Lion way to sleep
}
}
class Monkey implements Animal {
public void eat() {
// Monkey way to eat
}
public void sleep() {
// Monkey way to sleep
}
}
Согласно псевдокоду, упомянутому выше, все, что способно есть или спать, будет называться животным, или мы можем сказать, что все животные должны есть и спать, но способ есть и спать зависит от животного.
В случае интерфейсов мы наследуем только поведение, а не реальный код, как в случае наследования классов.
Q1. Поскольку интерфейсы имеют только абстрактные методы (без кода), как мы можем сказать, что если мы реализуем какой-либо интерфейс, то это наследование? Мы не используем его код.
Реализация интерфейсов - это другой вид наследования. Это не похоже на наследование классов, так как в этом наследовании дочерний класс получает реальный код для повторного использования из базового класса.
Q2. Если реализация интерфейса не является наследованием, то как интерфейсы используются для достижения множественного наследования?
Это объясняется тем, что один класс может реализовывать более одного интерфейса. Но мы должны понимать, что это наследование отличается от наследования классов.
Q3. В любом случае, какова польза от использования интерфейсов? У них нет никакого кода. Нам нужно снова и снова писать код во всех классах, которые мы его реализуем.
Реализация интерфейса заставляет класс принуждать переопределять все его абстрактные методы.
Читайте больше в моей книге здесь и здесь
Ответ 2
Q1. Поскольку интерфейсы имеют только абстрактные методы (без кода), так как мы можем сказать, что если мы реализуем какой-либо интерфейс, то это наследование? Мы не используем его код.
Мы не можем. Интерфейсы не используются для достижения множественного наследования. Они заменяют его более безопасной, хотя и немного менее мощной конструкцией. Обратите внимание на ключевое слово implements
, а не на extends
.
Q2. Если реализация интерфейса не является наследованием, то как интерфейсы используются для достижения множественного наследования?
Это не так. С интерфейсами один класс может иметь несколько "представлений", разных API или возможностей. Например. Класс может быть Runnable
и Callable
одновременно, в то время как оба метода эффективно выполняют одно и то же.
Q3. Во всяком случае, в чем преимущество использования интерфейсов? У них нет никакого кода. Нам нужно писать код снова и снова во всех классах, которые мы реализуем.
Интерфейсы являются разновидностью множественного наследования без каких-либо проблем, которые возникают у последнего (например, проблема Diamond).
Существует несколько вариантов использования интерфейсов:
-
Объект эффективно имеет два тождества: a Tank
- это Vehicle
и a Weapon
. Вы можете использовать экземпляр Tank
, где ожидается либо первый, либо последний (полиморфизм). Это редко бывает в реальной жизни и на самом деле является допустимым примером, когда множественное наследование было бы лучше (или чертами).
-
Простые обязанности: экземпляр объекта Tank
в игре также Runnable
позволяет выполнить его в потоке и ActionListener
для ответа на события мыши.
-
Интерфейсы обратного вызова: если объект реализует данный интерфейс обратного вызова, он уведомляется о его жизненном цикле или других событиях.
-
Маркерные интерфейсы: не добавлять какие-либо методы, но легко доступны через instanceof
для обнаружения возможностей или пожеланий объекта. Serializable
и Cloneable
являются примерами этого.
То, что вы ищете, является чертой (например, в Scala), к сожалению недоступной в Java.
Ответ 3
ПОЦЕЛУЙ
Я искал дни, недели недель, пытаясь понять интерфейсы и, похоже, читал одну и ту же общую помощь; Я не пытаюсь унизить вклад, но я думаю, что лампочка просто щелкнула, так что я надулся:))
Я предпочитаю Keep It Simple Stupid, поэтому предложит мне новый интерфейс.
Я обычный кодер, но я хочу опубликовать этот код, который я написал в VB.NET(принцип одинаков для других языков), чтобы помочь другим понять интерфейсы.
Если я ошибаюсь, то, пожалуйста, сообщите другим в комментариях.
Объяснение
Три кнопки в форме, нажав на каждую из них, сохраняют другую ссылку на класс интерфейса (_data). Вся суть различных ссылок на классы в переменную интерфейса - это то, что я не понял, поскольку это казалось излишним, тогда его сила становится очевидной с помощью msgbox, мне нужно только вызвать метод SAME для выполнения нужной мне задачи. case 'GetData()', который использует метод в классе, который в настоящее время поддерживается ссылочной переменной интерфейса (_data).
Поэтому, однако, я хочу получить свои данные (из базы данных, Интернета или текстового файла), это только когда-либо сделанное с использованием того же имени метода; код за этой реализацией... мне все равно.
Затем легко изменить каждый класс кода с помощью интерфейса без какой-либо зависимости... это ключевая цель в OO и инкапсуляции.
Когда использовать
Классы кода, и если вы заметите тот же самый глагол, который используется для методов, например "GetData()", то он является хорошим кандидатом для реализации интерфейса в этом классе и использует это имя метода как абстракцию/интерфейс.
Я искренне надеюсь, что это поможет дружбу noob с этим трудным принципом.
Public Class Form1
Private _data As IData = Nothing
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
_data = New DataText()
MsgBox(_data.GetData())
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
_data = New DataDB()
MsgBox(_data.GetData())
End Sub
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
_data = New DataWeb()
MsgBox(_data.GetData())
End Sub
End Class
Public Interface IData
Function GetData() As String
End Interface
Friend Class DataText : Implements IData
Friend Function GetData() As String Implements IData.GetData
Return "DataText"
End Function
End Class
Friend Class DataDB : Implements IData
Friend Function GetData() As String Implements IData.GetData
Return "DataDB"
End Function
End Class
Friend Class DataWeb : Implements IData
Friend Function GetData() As String Implements IData.GetData
Return "DataWeb"
End Function
End Class
Ответ 4
Q1. Поскольку интерфейсы имеют только абстрактные методы (без кода), как мы можем сказать, что если мы реализуем какой-либо интерфейс, то это наследование? Мы не используем его код.
К сожалению, в разговорной речи inheritance
слова по-прежнему часто используется, когда класс реализует интерфейс, хотя interface implementation
была бы предпочтительным термином - IMO, термин inheritance
должен строго использоваться с наследованием конкретного или абстрактного класса. В таких языках, как C++ и С#, один и тот же синтаксис (т.е. Subclass: Superclass
и Class: Interface
) используется как для наследования классов, так и для реализации интерфейса, что, возможно, способствовало распространению неправильного использования inheritance
слов в интерфейсах. Java имеет другой синтаксис для extend
класса в отличие от implement
интерфейса, что является хорошей вещью.
В2. Если реализация интерфейса не является наследованием, то как интерфейсы используются для множественного наследования?
Вы можете добиться "эффекта" множественного наследования посредством композиции - реализовав несколько интерфейсов в классе, а затем предоставив реализации для всех методов, свойств и событий, требуемых для всех интерфейсов в классе. Один из распространенных способов сделать это с конкретными классами - это создать отношения has-a (композиция) с классами, которые реализуют внешние интерфейсы, "связывая" реализацию с каждой из реализаций внутреннего класса. (Такие языки, как C++ действительно поддерживают множественное конкретное наследование, но это создает другие потенциальные проблемы, такие как проблема алмазов).
Q3 В любом случае, каковы преимущества использования интерфейсов? У них нет никакого кода. Нам нужно снова и снова писать код во всех классах, которые мы его реализуем.
Интерфейсы позволяют существующим классам (например, фреймворкам) взаимодействовать с вашими новыми классами, даже не видя их раньше, благодаря способности общаться через известный интерфейс. Думайте об интерфейсе как о контракте. Внедрив этот интерфейс в классе, вы по контракту обязаны выполнять требуемые от него обязательства, и как только этот контракт будет реализован, ваш класс сможет взаимозаменяемо использоваться с любым другим кодом, который использует интерфейс.
Пример из реального мира
Примером "реального мира" может быть законодательство и конвенция (интерфейс), касающиеся электрической розетки в конкретной стране. Каждое электрическое устройство, подключенное к розетке, должно соответствовать спецификациям (контракту), которые власти определили для розетки, например, расположение линии, нейтральных и заземляющих проводов, положение и окраска двухпозиционного переключателя, а также соответствие электрическое напряжение, частота и максимальный ток, который будет подаваться через interface
при его включении.
Преимущество отсоединения интерфейса (то есть от стандартной настенной розетки) вместо простой пайки проводов заключается в том, что вы можете подключить (и отключить) вентилятор, чайник, двойной адаптер или какое-то новое устройство, которое будет изобретено в следующем году. хотя это устройство не существовало на момент разработки интерфейса. Зачем? Потому что это будет соответствовать требованиям интерфейса.
Зачем использовать интерфейсы?
Интерфейсы отлично подходят для слабой связи классов и являются одной из опор парадигмы SOLID Дядюшки Боба, особенно Dependency Inversion Principle
и Interface Segregation Principles
.
Проще говоря, гарантируя, что зависимости между классами связаны только на интерфейсах (абстракциях), а не на других конкретных классах, это позволяет заменить зависимость любой другой реализацией класса, которая отвечает требованиям интерфейса.
При тестировании можно использовать заглушки и макеты зависимостей для модульного тестирования каждого класса, а за взаимодействием класса с зависимостью можно "шпионить".
Ответ 5
Это очень старый вопрос, и в выпуске java-8 добавлено больше возможностей и возможностей для интерфейса.
Объявление интерфейса может содержать
- сигнатуры методов
- методы по умолчанию
- статические методы
- постоянные определения.
Единственными методами, которые имеют реализации в интерфейсе, являются методы по умолчанию и статические.
Использование интерфейса:
- Чтобы определить контракт
- Чтобы связать несвязанные классы с возможностями (например, классы, реализующие интерфейс Serializable, могут иметь или не иметь никакой связи между ними, кроме реализации этого интерфейса
- Чтобы обеспечить реализацию взаимозаменяемого, например.
Strategy_pattern
Методы - по умолчанию позволяют добавлять новые функциональные возможности в интерфейсы ваших библиотек и обеспечивать двоичную совместимость с кодом, написанным для более старых версий этих интерфейсов.
- Организуйте вспомогательные методы в своих библиотеках с помощью методов static (вы можете сохранять статические методы специфичными для интерфейса в одном и том же интерфейсе, а не в отдельном классе)
Посмотрите на этот связанный вопрос SE для примера кода, чтобы лучше понять концепции:
Как я должен объяснить разницу между интерфейсом и абстрактным классом?
Возвращаясь к вашим запросам:
Q1. Поскольку интерфейсы имеют только абстрактные методы (без кода), так как мы можем сказать, что если мы реализуем какой-либо интерфейс, то это наследование? Мы не используем его код.
Q2. Если реализация интерфейса не является наследованием, то как интерфейсы используются для достижения множественного наследования?
Интерфейс может содержать код для статических и стандартных методов. Эти методы по умолчанию обеспечивают обратную совместимость, а статические методы предоставляют вспомогательные/служебные функции.
Вы не можете иметь истинное множественное наследование в Java, а интерфейс - не способ его получить. Интерфейс может содержать только константы. Таким образом, вы не можете наследовать состояние, но вы можете реализовать поведение.
Вы можете заменить наследование возможностями. Интерфейс предоставляет множество возможностей для реализации классов.
Q3. Во всяком случае, в чем преимущество использования интерфейсов? У них нет никакого кода. Нам нужно писать код снова и снова во всех классах, которые мы реализуем.
Обратитесь к разделу " использование интерфейса" в моем ответе.
Ответ 6
Наследование - это когда один класс происходит от другого класса (который может быть абстрактным) или интерфейса. Самая сильная точка объектно-ориентированного (наследования) - это не повторное использование кода (есть много способов сделать это), а полиморфизм.
Полиморфизм - это когда у вас есть код, который использует интерфейс, который может представлять собой экземпляр объекта любого класса, полученного из этого интерфейса. Например, у меня может быть такой метод:
public void Pet (животное IAnimal), и этот метод получит объект, который является экземпляром Dog или Cat, который наследуется от IAnimal. или я могу иметь такой код:
Изначальное животное
и затем я могу вызвать метод этого интерфейса:
animal.Eat(), которую Собака или Кошка могут реализовать по-другому.
Основным преимуществом интерфейсов является то, что вы можете наследовать некоторые из них, но если вам нужно наследовать только один, вы также можете использовать абстрактный класс. Вот статья, в которой объясняется больше различий между абстрактным классом и интерфейсом:
http://www.codeproject.com/KB/cs/abstractsvsinterfaces.aspx
Ответ 7
Оба метода работают (интерфейсы и множественное наследование).
Быстрый Практический Короткий Ответ
Интерфейсы лучше, когда у вас есть многолетний опыт использования множественного наследования, в котором есть суперклассы только с определением метода и вообще без кода.
Дополнительный вопрос может быть следующим: "Как и зачем переносить код из абстрактных классов в интерфейсы".
Если вы не используете много абстрактных классов в своем приложении или не имеете большого опыта работы с ним, вы можете пропустить интерфейсы.
Не спешите использовать интерфейсы.
Длинный скучный ответ
Интерфейсы очень похожи или даже эквивалентны абстрактным классам.
Если в вашем коде много абстрактных классов, то самое время подумать об интерфейсах.
Следующий код с абстрактными классами:
MyStreamsClasses.java
/* File name : MyStreamsClasses.java */
import java.lang.*;
// Any number of import statements
public abstract class InputStream {
public void ReadObject(Object MyObject);
}
public abstract class OutputStream {
public void WriteObject(Object MyObject);
}
public abstract class InputOutputStream
imnplements InputStream, OutputStream {
public void DoSomethingElse();
}
Можно заменить на:
MyStreamsInterfaces.java
/* File name : MyStreamsInterfaces.java */
import java.lang.*;
// Any number of import statements
public interface InputStream {
public void ReadObject(Object MyObject);
}
public interface OutputStream {
public void WriteObject(Object MyObject);
}
public interface InputOutputStream
extends InputStream, OutputStream {
public void DoSomethingElse();
}
Приветствия.
Ответ 8
Старый вопрос Я удивлен, что никто не процитировал канонические источники: Java: обзор Джеймса Гослинга, Шаблоны проектирования: элементы многоразового объектно-ориентированного программного обеспечения от Gang of Four или Эффективная Java от Джошуа Блоха (среди других источников).
Я начну с цитаты:
Интерфейс - это просто спецификация набора методов, на которые реагирует объект. Он не включает никаких переменных экземпляра или реализации. Интерфейсы могут быть множественным наследованием (в отличие от классов), и они могут использоваться более гибким способом, чем обычная жесткая структура наследования классов. (Гослинг, стр .8)
Теперь, давайте рассмотрим ваши предположения и вопросы один за другим (я добровольно проигнорирую функции Java 8).
Предположения
Интерфейс - это коллекция ТОЛЬКО абстрактных методов и конечных полей.
Вы видели ключевое слово abstract
в интерфейсах Java? Нет. Тогда вы не должны рассматривать интерфейс как набор абстрактных методов. Возможно, вас вводят в заблуждение так называемые интерфейсы C++, которые являются классами только с чисто виртуальными методами. C++, по замыслу, не имеет (и не должно иметь) интерфейсов, поскольку имеет множественное наследование.
Как объяснил Гослинг, вы должны рассматривать интерфейс как "набор методов, на которые реагирует объект". Мне нравится видеть интерфейс и связанную документацию как сервисный контракт. Он описывает, что вы можете ожидать от объекта, который реализует этот интерфейс. В документации должны быть указаны предварительные и последующие условия (например, параметры должны быть не нулевыми, выходные данные всегда положительными,...) и инварианты (метод, который не изменяет внутреннее состояние объекта). Этот контракт является сердцем, я думаю, ООП.
В Java нет множественного наследования.
В самом деле.
JAVA опускает многие редко используемые, плохо понятые, запутанные особенности C++, которые по нашему опыту приносят больше горя, чем пользы. Это в первую очередь состоит из перегрузки операторов (хотя она и имеет перегрузку методов), множественного наследования и обширных автоматических приведений. (Гослинг, с .2)
Нечего добавить.
Интерфейсы могут использоваться для достижения множественного наследования в Java.
Нет, simlpy, потому что в Java нет множественного наследования. Смотри выше.
Сильной стороной наследования является то, что мы можем использовать код базового класса в производном классе, не записывая его снова. Может быть, это самое важное для наследства.
Это называется "наследование реализации". Как вы написали, это удобный способ повторно использовать код.
Но у него есть важный аналог:
родительские классы часто определяют хотя бы часть физического представления своих подклассов. Поскольку наследование предоставляет подкласс деталям его родительской реализации, в нем часто говорится, что "наследование нарушает инкапсуляцию" [Sny86]. Реализация подкласса становится настолько связанной с реализацией его родительского класса, что любое изменение в родительской реализации заставит подкласс измениться. (GOF, 1.6)
(Есть аналогичная цитата в Блохе, пункт 16.)
На самом деле наследование служит и другой цели:
Наследование классов сочетает в себе наследование интерфейса и наследование реализации. Наследование интерфейса определяет новый интерфейс в терминах одного или нескольких существующих интерфейсов. Наследование реализации определяет новую реализацию в терминах одной или нескольких существующих реализаций. (GOF, Приложение A)
Оба используют ключевое слово extends
в Java. У вас могут быть иерархии классов и иерархии интерфейсов. Первые разделяют реализацию, вторые разделяют обязательства.
Вопросы
Q1. Поскольку интерфейсы имеют только абстрактные методы (без кода), как мы можем сказать, что если мы реализуем какой-либо интерфейс, то это наследование? Мы не используем его код. **
Реализация интерфейса не наследование. Это реализация. Таким образом, ключевое слово implements
.
Q2. Если реализация интерфейса не является наследованием, то как интерфейсы используются для достижения множественного наследования? **
Нет множественного наследования в Java. Смотри выше.
Q3. В любом случае, какова польза от использования интерфейсов? У них нет никакого кода. Нам нужно снова и снова писать код во всех классах, которые мы его реализуем./Тогда зачем создавать интерфейсы?/Каковы точные преимущества использования интерфейсов? Действительно ли мы получаем множественное наследование с помощью интерфейсов?
Самый важный вопрос: почему вы хотели бы иметь множественное наследование? Я могу придумать два ответа: 1. дать объектам несколько типов; 2. повторно использовать код.
Присвойте объектам типы mutliple
В ООП один объект может иметь разные типы. Например, в Java ArrayList<E>
имеет следующие типы: Serializable
, Cloneable
, Iterable<E>
, Collection<E>
, List<E>
, RandomAccess
, AbstractList<E>
, AbstractCollection<E>
и Object
(я надеюсь, Я не забыл никого). Если объект имеет разные типы, различные потребители смогут использовать его, не зная о его особенностях. Мне нужен Iterable<E>
а вы даете мне ArrayList<E>
? Все хорошо Но если мне нужен сейчас List<E>
и вы дадите мне ArrayList<E>
, это тоже хорошо. И т.п.
Как вы вводите объект в ООП? В качестве примера вы взяли интерфейс Runnable
, и этот пример идеально подходит для иллюстрации ответа на этот вопрос. Я цитирую официальный документ Java:
Кроме того, Runnable предоставляет средства для того, чтобы класс был активным, не наследуя Thread.
Вот в чем суть: Наследование - это удобный способ типирования объектов. Вы хотите создать тему? Пусть подкласс класса Thread
. Вы хотите, чтобы объект имел разные типы, позвольте использовать mutliple-наследование. Argh. Это не существует в Java. (В C++, если вы хотите, чтобы объект имел разные типы, множественное наследование - это путь.)
Как придать объектам типы mutliple? В Java вы можете напечатать свой объект напрямую. Это то, что вы делаете, когда ваш класс implements
интерфейс Runnable
. Зачем использовать Runnable
если вы поклонник наследования? Может быть, потому что ваш класс уже является подклассом другого класса, скажем, A
Теперь у вашего класса есть два типа: A
и Runnable
.
С несколькими интерфейсами вы можете назначить объекту несколько типов. Вам просто нужно создать класс, который implements
несколько интерфейсов. Пока вы соблюдаете условия контрактов, это нормально.
Повторно использовать код
Это сложный предмет; Я уже процитировал GOF о нарушении инкапсуляции. Другой ответ затронул проблему алмазов. Вы также можете подумать о принципе единой ответственности:
У класса должна быть только одна причина измениться. (Роберт К. Мартин, Agile Software Development, принципы, шаблоны и практики)
Наличие родительского класса может дать классу причину для изменения, помимо его собственных обязанностей:
Реализация суперкласса может изменяться от выпуска к выпуску, и если это так, подкласс может сломаться, даже если его код не был затронут. Как следствие, подкласс должен развиваться в тандеме со своим суперклассом (Bloch, пункт 16).
Я бы добавил более прозаическую проблему: у меня всегда возникает странное чувство, когда я пытаюсь найти исходный код метода в классе и не могу его найти. Тогда я помню: это должно быть определено где-то в родительском классе. Или в классе бабушек и дедушек. Или, может быть, даже выше. Хорошая IDE в этом случае является ценным активом, но, на мой взгляд, остается чем-то волшебным. Ничего подобного с иерархиями интерфейсов, поскольку единственное, что мне нужно, - это javadoc: один ярлык клавиатуры в IDE, и я его получаю.
У наследства есть преимущества:
Безопасно использовать наследование в пакете, где реализации подкласса и суперкласса находятся под контролем одних и тех же программистов. Также безопасно использовать наследование при расширении классов, специально разработанных и задокументированных для расширения (пункт 17: Разработка и документация для наследования или иное запрещение). (Блох, п .16)
Примером класса, "специально разработанного и задокументированного для расширения" в Java, является AbstractList
.
Но Блох и GOF настаивают на этом: "Пользуйся композицией, а не наследством":
Делегирование - это способ сделать композицию такой же мощной для повторного использования, как наследование [Lie86, JZ91]. При делегировании два объекта участвуют в обработке запроса: принимающий объект делегирует операции своему делегату. Это аналогично подклассам, откладывающим запросы к родительским классам. (GOF стр .32)
Если вы используете композицию, вам не придется писать один и тот же код снова и снова. Вы просто создаете класс, который обрабатывает дубликаты, и передаете экземпляр этого класса классам, которые реализуют интерфейс. Это очень простой способ повторного использования кода. И это поможет вам следовать принципу единой ответственности и сделать код более тестируемым. У Rust и Go нет наследования (у них тоже нет классов), но я не думаю, что код более избыточен, чем в других языках ООП.
Кроме того, если вы используете композицию, вы, естественно, будете использовать интерфейсы, чтобы придать вашему коду необходимую структуру и гибкость (см. Другие ответы о вариантах использования интерфейсов).
Примечание: вы можете поделиться кодом с интерфейсами Java 8
И, наконец, последняя цитата:
Во время незабываемой сессии вопросов и ответов кто-то спросил его [Джеймса Гослинга]: "Если бы вы могли снова заняться Java, что бы вы изменили?" "Я бы пропустил занятия" (где-нибудь в сети, не знаю, правда ли это)
Ответ 9
Интерфейсы сделаны так, что класс будет реализовывать функциональность в интерфейсе и вести себя в соответствии с этим интерфейсом.
Ответ 10
Интерфейсы необходимы, чтобы обеспечить правильный стандарт.
В разработке программного обеспечения существует ряд ситуаций, когда важно, чтобы разрозненные группы программистов согласились на "контракт", в котором говорится о взаимодействии их программного обеспечения.
Каждая группа должна иметь возможность писать свой код без каких-либо знаний о том, как написан другой код группы. Вообще говоря, интерфейсы являются такими контрактами.
Например:
Представьте себе футуристическое общество, где управляемые компьютером роботизированные автомобили перевозят пассажиров по улицам города без человека. Производители автомобилей пишут программное обеспечение (Java, конечно;), которое управляет автомобильной остановкой, запускает, ускоряется, поворачивается налево и т.д. Другая промышленная группа, разработчики электронных накладных инструментов, создает компьютерные системы, которые получают данные о местоположении GPS (Global Positioning System) и беспроводную передачу условий движения и используют эту информацию для управления автомобилем.
Автопроизводители должны опубликовать отраслевой стандарт, который подробно изложит, какие методы можно использовать для перемещения автомобиля (любой автомобиль, от любого производителя)., Затем производители руководств могут написать программное обеспечение, которое вызывает методы, описанные в интерфейсе, для управления автомобилем. Ни одна промышленная группа не должна знать, как реализовано другое групповое программное обеспечение. На самом деле, каждая группа считает свое программное обеспечение высокопричастным и оставляет за собой право модифицировать его в любое время, если оно продолжается придерживаться опубликованного интерфейса
Ответ 11
Q1. Поскольку интерфейсы имеют только абстрактные методы (без кода), как мы можем сказать, что если мы реализуем интерфейс, то это наследование? Мы не используем его код.
Это не равное наследство. Это просто похоже. Позволь мне объяснить:
VolvoV3 extends VolvoV2, and VolvoV2 extends Volvo (Class)
VolvoV3 extends VolvoV2, and VolvoV2 implements Volvo (Interface)
line1: Volvo v = new VolvoV2();
line2: Volvo v = new VolvoV3();
Если вы видите только line1 и line2, вы можете сделать вывод, что VolvoV2 и VolvoV3 имеют одинаковый тип. Вы не можете определить, является ли Volvo суперклассом или Volvo интерфейсом.
Q2. Если реализация интерфейса не является наследованием, то как интерфейсы используются для достижения множественного наследования?
Теперь с помощью интерфейсов:
VolvoXC90 implements XCModel and Volvo (Interface)
VolvoXC95 implements XCModel and Volvo (Interface)
line1: Volvo a = new VolvoXC90();
line2: Volvo a = new VolvoXC95();
line3: XCModel a = new VolvoXC95();
Если вы видите только line1 и line2, вы можете сделать вывод, что VolvoXC90 и VolvoXC95 имеют одинаковый тип (Volvo). Вы не можете сделать вывод, что Volvo является суперклассом или Volvo является интерфейсом.
Если вы видите только line2 и line3, вы можете сделать вывод, что Volvo95 реализует два типа, XCModel и Volvo, в Java вы знаете, что по крайней мере один должен быть интерфейсом. Если бы этот код был написан, например, в C++, это могли быть оба класса. Поэтому множественное наследование.
Q3. В любом случае, в чем преимущество использования интерфейсов? У них нет никакого кода. Нам нужно снова и снова писать код во всех классах, которые мы его реализуем.
Представьте себе систему, в которой вы используете класс VolvoXC90 в 200 других классах.
VolvoXC90 v = new VolvoXC90();
Если вам нужно развивать свою систему для запуска VolvoXC95, вам нужно изменить 200 других классов.
Теперь представьте систему, в которой вы используете интерфейс Volvo в 10 000 000 классов.
// Create VolvoXC90 but now we need to create VolvoXC95
Volvo v = new VolvoFactory().newCurrentVolvoModel();
Теперь, если вам нужно развивать свою систему для создания моделей VolvoXC95, вам придется изменить только один класс - Factory.
Это вопрос здравого смысла. Если ваша система состоит только из нескольких классов и имеет мало обновлений, использование интерфейсов повсеместно контрпродуктивно. Для больших систем это может избавить вас от многих трудностей и избежать риска принятия интерфейсов.
Я рекомендую вам прочитать больше о принципах SOLID и прочитать книгу Эффективная Java. У него есть хорошие уроки от опытных инженеров программного обеспечения.
Ответ 12
Так. Здесь есть много отличных ответов, подробно объясняющих, что такое интерфейс. Тем не менее, это пример его использования, как один из моих лучших коллег когда-либо объяснил мне это много лет назад, с тем, что я усвоил в университете за последние пару лет.
Интерфейс - это своего рода "контракт". Он предоставляет некоторые доступные методы, поля и так далее. Он не раскрывает никаких деталей реализации, только то, что он возвращает, и какие параметры он принимает. И здесь лежит ответ на третий вопрос, и то, что я чувствую, является одной из самых сильных сторон современного ООП:
"Код дополнением, а не модификацией" - Магнус Мадсен, AAU
Это то, что он назвал, по крайней мере, и он может получить его из какого-то другого места. Пример кода ниже написан на С#, но все показанное можно сделать примерно так же, как в Java.
То, что мы видим, это класс с именем SampleApp, который имеет одно поле, IOContext. IOContext - это интерфейс. SampleApp не заботится о том, как он сохраняет свои данные, он просто должен сделать это в своем методе doSomething().
Мы можем представить, что в начале процесса разработки сохранение данных, возможно, было более важным, чем то, КАК они были сохранены, и поэтому разработчик решил просто написать класс FileContext. Позже, однако, ему нужно было поддерживать JSON по любой причине. Поэтому он написал класс JSONFileContext, который наследует FileContext. Это означает, что это фактически IOContext, который имеет функциональность FileContext, сохраняет замену FileContexts SaveData и LoadData, он все еще использует свои методы "запись/чтение".
Реализация JSON-класса была небольшой работой, по сравнению с написанием этого класса и просто наследованием IOContext.
Поле SampleApp могло бы быть просто типа FileContext, но в этом случае оно было бы ограничено использованием только потомков этого класса. Создавая интерфейс, мы можем даже реализовать реализацию SQLiteContext и записать в базу данных, SampleApp никогда не узнает и не позаботится, а когда мы напишем класс sql lite, нам потребуется внести только одно изменение в наш код: new JSONFileContext();
вместо этого становится new SQLiteContext();
У нас все еще есть наши старые реализации, и мы можем вернуться к ним, если возникнет такая необходимость. Мы ничего не сломали, и все изменения в нашем коде - это пол строки, которые можно изменить в мгновение ока.
Итак: код дополнением, а НЕ модификацией.
namespace Sample
{
class SampleApp
{
private IOContext context;
public SampleApp()
{
this.context = new JSONFileContext(); //or any of the other implementations
}
public void doSomething()
{
//This app can now use the context, completely agnostic of the actual implementation details.
object data = context.LoadData();
//manipulate data
context.SaveData(data);
}
}
interface IOContext
{
void SaveData(object data);
object LoadData();
}
class FileContext : IOContext
{
public object LoadData()
{
object data = null;
var fileContents = loadFileContents();
//Logic to turn fileContents into a data object
return data;
}
public void SaveData(object data)
{
//logic to create filecontents from 'data'
writeFileContents(string.Empty);
}
protected void writeFileContents(string fileContents)
{
//writes the fileContents to disk
}
protected string loadFileContents()
{
string fileContents = string.Empty;
//loads the fileContents and returns it as a string
return fileContents;
}
}
class JSONFileContext : FileContext
{
public new void SaveData(object data)
{
//logic to create filecontents from 'data'
base.writeFileContents(string.Empty);
}
public new object LoadData()
{
object data = null;
var fileContents = loadFileContents();
//Logic to turn fileContents into a data object
return data;
}
}
class SQLiteContext : IOContext
{
public object LoadData()
{
object data = null;
//logic to read data into the data object
return data;
}
public void SaveData(object data)
{
//logic to save the data object in the database
}
}
}
Ответ 13
Интерфейсы
Интерфейс - это контракт, определяющий, как взаимодействовать с объектом. Они полезны для выражения того, как ваши внутренние объекты намерены взаимодействовать с объектом. После инверсии зависимости ваш публичный API будет иметь все параметры, выраженные через интерфейсы. Вам все равно, как он делает то, что вам нужно, просто он делает именно то, что вам нужно.
Пример: вам может понадобиться Vehicle
для перевозки товаров, вас не волнует конкретный вид транспорта.
наследование
Наследование является расширением конкретной реализации. Эта реализация может удовлетворять или не удовлетворять конкретному интерфейсу. Вы должны ожидать, что предок конкретной реализации, только когда вы заботитесь о том, как.
Пример: вам может потребоваться реализация автомобиля на Plane
для быстрой перевозки.
Состав
Композиция может использоваться как альтернатива наследования. Вместо вашего класса, расширяющего базовый класс, он создается с объектами, которые реализуют меньшие части ответственности основного класса. Композиция используется в оформлении facade pattern
и оформлении decorator pattern
.
Пример: Вы можете создать DuckBoat
(DUKW), который реализует LandVehicle
и WaterVehicle
которые оба реализуют Vehicle
состоящий из реализаций Truck
и Boat
.
ответы
Q1. Поскольку интерфейсы имеют только абстрактные методы (без кода), как мы можем сказать, что если мы реализуем какой-либо интерфейс, то это наследование? Мы не используем его код.
Интерфейсы не наследование. Реализация интерфейса выражает намерение вашего класса работать так, как это определено интерфейсом. Наследование - это когда у вас общий предок, и вы получаете то же поведение (inherit
), что и предок, поэтому вам не нужно его определять.
Q2. Если реализация интерфейса не является наследованием, то как интерфейсы используются для достижения множественного наследования?
Интерфейсы не достигают множественного наследования. Они выражают, что класс может подходить для нескольких ролей.
Q3. В любом случае, какова польза от использования интерфейсов? У них нет никакого кода. Нам нужно снова и снова писать код во всех классах, которые мы его реализуем.
Одним из основных преимуществ интерфейсов является разделение задач:
- Вы можете написать класс, который делает что-то с другим классом, не заботясь о том, как этот класс реализован.
- Любая будущая разработка может быть совместима с вашей реализацией без необходимости расширения определенного базового класса.
В духе DRY
вы можете написать реализацию, которая удовлетворяет интерфейсу, и изменить его, сохраняя при этом open/closed principal
если вы используете композицию.