Вопрос

Это в некотором роде дополнительный вопрос к этому вопрос.

Предположим, у меня есть дерево наследования следующим образом:

Car -> Ford -> Mustang -> MustangGT

Есть ли польза в определении интерфейсов для каждый из этих классов?Пример:

ICar -> IFord -> IMustang -> IMustangGT

Я вижу, что, возможно, другие классы (например Chevy) хотел бы реализовать Icar или IFord и, может быть , даже IMustang, но , вероятно , нет IMustangGT потому что это так специфично.Являются ли интерфейсы лишними в данном случае?

Кроме того, я бы подумал, что любой класс, который захотел бы реализовать IFord определенно захотел бы использовать свое единственное наследование, унаследовав от Ford чтобы не дублировать код.Если это данность, то в чем польза от того, чтобы также внедрять IFord?

Это было полезно?

Решение

По моему опыту, интерфейсы лучше всего использовать, когда у вас есть несколько классов, каждый из которых должен реагировать на один и тот же метод или методы, чтобы они могли взаимозаменяемо использоваться другим кодом, который будет написан для общего интерфейса этих классов.Наилучший способ использования интерфейса - это когда протокол важен, но лежащая в его основе логика может отличаться для каждого класса.Если в противном случае вы бы дублировали логику, рассмотрите вместо этого абстрактные классы или стандартное наследование классов.

И в ответ на первую часть вашего вопроса я бы рекомендовал не создавать интерфейс для каждого из ваших классов.Это излишне загромождало бы структуру вашего класса.Если вы обнаружите, что вам нужен интерфейс, вы всегда можете добавить его позже.Надеюсь, это поможет!

Адам

Другие советы

Я также согласен с ответ адамалекса эти интерфейсы должны совместно использоваться классами, которые должны реагировать на определенные методы.

Если классы обладают схожей функциональностью, но не связаны друг с другом напрямую наследственными отношениями, то интерфейс был бы хорошим способом добавить эту функцию к классам без дублирования функциональности между ними.(Или иметь несколько реализаций с незначительными различиями.)

Пока мы используем аналогию с автомобилем, приведем конкретный пример.Допустим, у нас есть следующие классы:

Car -> Ford   -> Escape  -> EscapeHybrid
Car -> Toyota -> Corolla -> CorollaHybrid

Автомобили имеют wheels и может Drive() и Steer().Таким образом, эти методы должны существовать в Car класс.(Вероятно, тот самый Car класс будет абстрактным классом.)

Идя дальше по линии, мы получаем различие между Ford и Toyota (вероятно, реализовано как разница в типе эмблемы на автомобиле, опять же, вероятно, абстрактный класс.)

Затем, наконец, у нас есть Escape и Corolla класс, который представляет собой классы, полностью реализованные как car.

Итак, как мы могли бы создать Гибрид транспортное средство?

У нас мог бы быть подкласс Escape это EscapeHybrid который добавляет FordsHybridDrive() метод и подкласс Corolla это CorollaHybrid с ToyotasHybridDrive() способ.Эти методы в основном делают одно и то же, но все же у нас разные методы. Фу. Похоже, мы можем сделать что-то получше.

Предположим, что гибрид имеет HybridDrive() способ.Поскольку мы не хотим в конечном итоге иметь два разных типа гибридов (в идеальном мире), поэтому мы можем создать IHybrid интерфейс, который имеет HybridDrive() способ.

Итак, если мы хотим сделать EscapeHybrid или CorollaHybrid класс, все , что нам нужно сделать , это внедрить IHybrid интерфейс.

Для примера из реального мира давайте взглянем на Java.Класс, который может выполнять сравнение объекта с другим объектом, реализует Comparable интерфейс.Как следует из названия, интерфейс должен быть для класса, который сопоставимый, отсюда и название "Сравнимый".

Просто из интереса, а пример автомобиля используется в Интерфейсы урок из Учебное пособие по Java.

Вы вообще не должны реализовывать ни один из этих интерфейсов.

Класс наследования описывает , что такое объект (например, это идентичность). Это хорошо, однако в большинстве случаев то, чем является объект , гораздо менее важно, чем то, что делает объект . Здесь вступают интерфейсы.

Интерфейс должен описывать , что делает объект ) или как он действует. Под этим я подразумеваю его поведение и набор операций, которые имеют смысл при таком поведении.

Таким образом, правильные имена интерфейсов должны иметь форму IDriveable , IHasWheels и т. д. Иногда лучший способ описать это поведение - обратиться к общеизвестному другому объекту, так что вы можете сказать, что «действует как один из них». (например: IList ), но ИМХО эта форма именования находится в меньшинстве.

Учитывая эту логику, сценарии, в которых наследование интерфейса имеет смысл, полностью и полностью отличаются от сценариев, в которых объектное наследование имеет смысл - часто эти сценарии не связаны с и то и другое.

Надеюсь, это поможет вам продумать интерфейсы, которые вам действительно нужны: -)

Я бы сказал, создайте интерфейс только для того, на что вам нужно сослаться. У вас могут быть какие-то другие классы или функции, которые нужно знать об автомобиле, но как часто будет что-то, что нужно знать о Форде?

Не создавайте вещи, которые вам не нужны. Если выясняется, что вам нужны интерфейсы, потребуется небольшое усилие, чтобы вернуться и построить их.

Кроме того, с педантичной стороны, я надеюсь, что вы на самом деле не создаете нечто, похожее на эту иерархию. Это не то, для чего следует использовать наследование.

Создавайте его только тогда, когда этот уровень функциональности станет необходимым.

Код повторного факторинга всегда находится в процессе.

Доступны инструменты, которые позволят вам извлечь интерфейс, если это необходимо. НАПРИМЕР. http://geekswithblogs.net/ JaySmith / архив / 2008/02/27 / рефакторинг-зрительно-студии приточно-interface.aspx

Сделайте ICar и все остальное (Make = Ford, Model = Mustang и прочее) как члены класса, который реализует интерфейс.

Возможно, вы захотите иметь свой класс Ford и, например, класс GM, и оба реализуют ICar, чтобы использовать полиморфизм, если вы не хотите идти по пути проверки Make == Независимо от , что это в вашем стиле.

В любом случае - на мой взгляд, это атрибуты автомобиля, а не наоборот - вам нужен только один интерфейс, потому что методы являются общими: Brake, SpeedUp и т. д.

Может ли Ford делать то, что не могут другие автомобили? Я так не думаю.

Я бы создал первые два уровня, ICar и IFord, и оставил бы второй уровень в покое, пока мне не понадобится интерфейс на этом втором уровне.

Тщательно продумайте, как ваши объекты должны взаимодействовать друг с другом в вашей проблемной области, и подумайте, нужно ли вам иметь более одной реализации конкретной абстрактной концепции. Используйте интерфейсы, чтобы обеспечить контракт вокруг концепции, с которой взаимодействуют другие объекты.

В вашем примере я бы предположил, что Ford, вероятно, является производителем, а Mustang - это значение ModelName, используемое производителем Ford, поэтому у вас может быть что-то похожее на:

IVehichle - > CarImpl, MotorbikeImpl - имеет-производитель имеет-много ModelNames

В этом ответе о разница между интерфейсом и классом, Я объяснил , что:

  • интерфейс раскрывает, что это за концепция является (в терминах "что является" действителен на момент компиляции) и используется для ценности (Мой интерфейс x = ...)
  • класс раскрывает, что это за концепция делает (фактически выполняется во время выполнения) и используется для значений или для объектов (MyClass x или aMyClass.method())

Итак, если вам нужно сохранить в переменной 'Ford' (понятие 'value') различные подклассы Ford, создайте IFord.В противном случае не беспокойтесь до тех пор, пока вам это действительно не понадобится.

Это один из критериев:если оно не будет выполнено, IFord, вероятно, бесполезен.
Если оно выполнено, то применяются другие критерии, указанные в предыдущих ответах:Если Ford имеет более богатый API, чем Car, IFord полезен для целей полиморфизма.Если нет, то достаточно ICar.

На мой взгляд, интерфейсы - это инструмент для реализации требования, чтобы класс реализовывал определенную сигнатуру или (как мне нравится думать) определенное «поведение». Для меня я думаю, что если я в начале своей прописной буквы назвал свое личное местоимение, и я пытаюсь назвать свои интерфейсы, чтобы их можно было читать таким образом ... ICanFly, IKnowHowToPersistMyself IAmDisplayable и т. Д. ... Так в вашем примере Я бы не стал создавать интерфейс для зеркалирования полной публичной подписи какого-либо конкретного класса. Я бы проанализировал общедоступную сигнатуру (поведение) и затем разделил бы участников на более мелкие логические группы (чем меньше, тем лучше), например (на вашем примере) IMove, IUseFuel, ICarryPassengers, ISteerable, IAccelerate, IDepreciate и т. Д. ... и затем применил эти интерфейсы к любым другим классам в моей системе нуждаются в них

В общем, лучший способ подумать об этом (и многие вопросы в ОО) - это подумать о понятии контракта.

Контракт определяется как соглашение между двумя (или более) сторонами, в котором указаны конкретные обязательства, которые должна выполнять каждая из сторон; в программе это то, какие услуги будет предоставлять класс, и что вы должны предоставить классу, чтобы получить эти услуги. Интерфейс устанавливает контракт, которому должен удовлетворять любой класс, реализующий интерфейс.

Однако, учитывая это, ваш вопрос в некоторой степени зависит от того, какой язык вы используете и что вы хотите делать.

После многих лет работы с ОО (как, боже мой, 30 лет) я обычно писал интерфейс для каждого контракта, особенно в Java, потому что это значительно облегчает тестирование: если у меня есть интерфейс для класса, Я могу создавать фиктивные объекты легко, почти тривиально.

Интерфейсы предназначены для использования в качестве общего публичного API, и пользователи будут ограничены в использовании этого публичного API. Если вы не хотите, чтобы пользователи использовали специфичные для типа методы IMustangGT , вы можете ограничить иерархию интерфейса ICar и IExoyCar .

Наследуется только от интерфейсов и абстрактных классов.

Если у вас есть пара классов, которые почти одинаковы, и вам нужно реализовать большинство методов, используйте и интерфейс в сочетании с покупкой другого объекта.
Если классы Mustang настолько отличаются, то создайте не только интерфейс ICar, но и IMustang.
Таким образом, классы Ford и Mustang могут наследоваться от ICar, а Mustang и MustangGT - от ICar и ИМустанг.

Если вы реализуете класс Ford и метод такой же, как у Mustang, покупайте у Mustang:

class Ford{
  public function Foo(){
    ...
    Mustang mustang  = new Mustang();
    return mustang.Foo();
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top