Как отойти от наследства - PullRequest
5 голосов
/ 27 мая 2011

Я искал здесь и на других форумах и не смог найти хорошего ответа. Я вроде знаю, что продление классов не лучшая практика.И то, что я должен использовать интерфейсы больше.моя проблема в том, что обычно я начинаю создавать интерфейсы, а затем перехожу к абстрактным классам, потому что всегда есть некоторая функциональность, которую я хочу реализовать в суперклассе, чтобы мне не приходилось дублировать ее в каждом дочернем классе.Например, у меня есть класс Vehicle и дочерние классы Car и Bike.В классе Vehicle может быть реализовано много функциональных возможностей, таких как Move () и Stop (), так как лучше всего поддерживать чистоту архитектуры, избегать повторения кода и использовать интерфейсы вместо наследования?Большое спасибо!

(если вы не знаете, почему я спрашиваю об этом, вы можете прочитать эту интересную статью: http://www.javaworld.com/javaworld/jw-08-2003/jw-0801-toolbox.html)

Ответы [ 8 ]

3 голосов
/ 27 мая 2011

Наследование («расширение классов») накладывает существенные ограничения на разработку классов, и я не уверен, что использование интерфейсов в качестве замены наследования является лучшей идеей, поскольку оно не проходит тест DRY.

В наши дни Композиция предпочтительнее Наследования, поэтому вы можете рассмотреть этот пост: Предпочитаете композицию по наследству?

2 голосов
/ 27 мая 2011

Интересный вопрос.У всех разные подходы.Но все это основано на личном опыте и выборе.

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

Это дает несколько преимуществ, основанных на опыте,
1. Во время вызовов функции вы можете передавать элементы как тип интерфейсаили абстрактный тип класса.
2.Общие переменные, такие как ID, Имена и т. д., могут быть помещены в абстрактный класс.
3.Легко для обслуживания.Например, если вы хотите реализовать новый интерфейс, просто быстро внедрите его в резюме.

1 голос
/ 27 мая 2011

Наследование позволяет повторно использовать код и заменять его, но ограничивает полиморфизм.Композиция позволяет повторное использование кода, но не заменяемость.Интерфейсы допускают заменяемость, но не повторное использование кода.

Решение о том, использовать ли наследование, композицию или интерфейсы, сводится к нескольким простым принципам:

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

  2. Если требуется повторное использование кода, но не заменяемость, используйте состав.

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

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

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

Может потребоваться суждение при принятии решения о том, могут ли будущие замещаемые классы испытать трудности при выводе из базового класса.Я склонен думать, что подход №5 часто предлагает лучшее из всех миров, когда требуется заменяемость.Это часто дешевле, чем использование одних только интерфейсов, и не намного дороже, чем использование только наследования.Если есть необходимость в будущих классах, которые могут быть заменяемыми, но не могут быть получены из базы, может потребоваться преобразовать код для использования подхода № 5.Использование подхода № 5 с самого начала позволит избежать необходимости рефакторинга кода позже.(Конечно, если нет необходимости заменять класс, который не может быть получен из базы, дополнительные расходы, какими бы незначительными они ни были, могут оказаться ненужными).

1 голос
/ 27 мая 2011

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

Как правило, вы используете абстрактный класс в вашей конкретной среде реализации, чтобы предоставить его потребителям некоторую базовую функциональность.Если вы просто заявляете, что никогда не используете абстрактный класс в пользу интерфейса - это просто неправильно с практической точки зрения.Что делать, если вам нужно 10 реализаций одного и того же интерфейса с 90% одного и того же кода.Повторять код 10 раз?Хорошо, может быть, вы бы использовали абстрактный класс здесь, но поместите интерфейс поверх него.Но зачем вам это делать, если вы никогда не собираетесь предлагать свою иерархию классов внешним потребителям?Я использую слово external в очень широком смысле - это может быть просто другой пакет в вашем проекте или удаленном потребителе.

В конечном счете, многие из этих вещей - это предпочтения и личный опыт, но я не согласен с большинством общих утверждений, таких как extends is evil.Я также предпочитаю не использовать дополнительные классы (интерфейсы или абстрактные), если это не требуется определенными частями проекта.

Только мои два цента.

0 голосов
/ 27 мая 2011

Я думаю, что интерфейсы иногда тоже злые.Это может быть как избежание множественного наследования.

Но если мы сравним интерфейс с абстрактным классом, то абстрактный класс всегда больше, чем интерфейс.Интерфейс - это всегда какой-то аспект класса - некоторая точка зрения, а не целое как класс.

Так что я не думаю, что вам следует избегать наследования и везде использовать его грани - должен быть баланс.

0 голосов
/ 27 мая 2011

Согласитесь с tofutim - в вашем текущем примере разумно передвигаться и останавливаться на транспортном средстве.

Прочитав статью - я думаю, что он использует мощный язык, чтобы выдвинуть точку ... помните - наследование - это инструментчтобы помочь выполнить работу.

Но если мы примем предположение, что по тем или иным причинам вы не можете / не будете использовать инструмент в этом случае, вы можете начать с разбивки его на небольшие интерфейсы свспомогательные объекты и / или посетители ...

Например - Типы транспортных средств включают подводную лодку, лодку, самолет, автомобиль и велосипед.Вы можете разбить его на интерфейсы ... IMoveable + Forward () + Backward () + Left () + Right ()

IFloatable + Dock ()

ISink () + BlowAir ()

IFly () + Takeoff () + Land ()

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

Проблема заключается в том, чточто вы можете в конечном итоге дублировать некоторый код в классе автомобиля / велосипеда для IMoveable.Left () и IMoveable.Right ().Вы можете преобразовать это в вспомогательный метод и объединить вспомогательный метод ... но если вы последуете его логическому завершению, вы все равно в конечном итоге проведете рефакторинг многих вещей обратно в базовые классы.

Наследование и агрегация - это инструменты.... ни один из которых не является "злом".

Надеюсь, это поможет.

0 голосов
/ 27 мая 2011
  • Не верьте всему, что вы читаете

    • Много раз наследование не является плохим, на самом деле для сокрытия данных это может быть хорошей идеей.
  • В основном, используйте политику «только интерфейсы», когда вы создаете очень маленькое дерево классов, в противном случае, я обещаю, что это будет больно.Предположим, у вас есть «класс» Person (имеет eat() и sleep), и есть два подкласса, Mathematician (имеет doProblem()) и Engineer (buildSomething()), затем используйте интерфейсы.Если вам нужно что-то вроде класса автомобилей, а затем 56 типов автомобилей, то переходите по наследству.

ИМХО.

0 голосов
/ 27 мая 2011

Хотите получить ответ для своего конкретного случая или вообще? В случае, который вы описали, нет ничего плохого в использовании класса Abstract. Не имеет смысла использовать интерфейс, когда все клиенты должны будут реализовывать один и тот же код для Move () и Stop ().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...