Когда использовать интерфейсы или абстрактные классы? Когда использовать обоих?

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

33
задан Peter Mortensen 20 November 2016 в 13:51
поделиться

5 ответов

Как первое практическое правило, я предпочитаю абстрактные классы интерфейсам на основе Руководства по проектированию .NET . Это рассуждение применимо гораздо шире, чем .NET, но лучше объяснено в книге Framework Design Guidelines .

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

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

] Итак, в качестве частичного ответа, прежде чем я продолжу: Наличие и интерфейса, и базового класса имеет смысл только в том случае, если вы решите изначально кодировать интерфейс. Если вы разрешаете интерфейс, вы должны кодировать только этот интерфейс, поскольку в противном случае вы нарушите принцип подстановки Лискова. Другими словами, даже если вы предоставляете базовый класс, реализующий интерфейс, вы не можете позволить своему коду использовать этот базовый класс.

Если вы решите кодировать базовый класс, наличие интерфейса не имеет смысла.

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

Пример, который приходит на ум, - это ASP.NET MVC. Конвейер запросов работает на IController, но есть sa Базовый класс контроллера, который вы обычно используете для реализации поведения.

Окончательный ответ: Если вы используете абстрактный базовый класс, используйте только его. При использовании интерфейса базовый класс является необязательной любезностью для разработчиков.


Обновление: Я больше не предпочитаю абстрактные классы интерфейсам, и уже давно не предпочитаю; вместо этого я предпочитаю композицию наследованию, используя SOLID в качестве ориентира.

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

При использовании интерфейса базовый класс является необязательной любезностью для разработчиков.


Обновление: Я больше не предпочитаю абстрактные классы интерфейсам, и уже давно не предпочитаю; вместо этого я предпочитаю композицию наследованию, используя SOLID в качестве ориентира.

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

При использовании интерфейса базовый класс является необязательной любезностью для разработчиков.


Обновление: Я больше не предпочитаю абстрактные классы интерфейсам, и уже давно не предпочитаю; вместо этого я предпочитаю композицию наследованию, используя SOLID в качестве ориентира.

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

36
ответ дан 27 November 2019 в 17:57
поделиться

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

Кот - это млекопитающее, но одна из его возможностей - это Pettable.

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

20
ответ дан 27 November 2019 в 17:57
поделиться

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

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

Внутри компонента это может быть нормально использовать абстрактный класс напрямую для доступа к иерархии классов.

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

interface Foo
abstract class FooBase implements Foo
class FunnyFoo extends FooBase
class SeriousFoo extends FooBase

Вы также можете иметь более абстрактные классы, наследующие друг от друга для более сложной иерархии.

2
ответ дан 27 November 2019 в 17:57
поделиться

Существует также так называемый DRY принцип - не повторяйся.

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

Преимущество состоит в том, что каждое исправление ошибки в универсальном коде приносит пользу всем конкретным реализациям.

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

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

2
ответ дан 27 November 2019 в 17:57
поделиться

Я всегда использую эти рекомендации:

  • Используйте интерфейсы для множественного наследования TYPE (поскольку .NET / Java не используют множественное наследование)
  • Используйте абстрактные классы для многократно используемой реализации типа

Правило доминирующей заботы диктует, что у класса всегда есть основная задача и 0 или более других (см. http://citeseer.ist.psu.edu/tarr99degrees.html ) . Эти 0 или более других вы затем реализуете через интерфейсы, поскольку класс затем реализует все типы, которые он должен реализовать (свой собственный и все интерфейсы, которые он реализует).

В мире множественного наследования реализаций (например, C ++ / Eiffel) можно наследовать от классов, реализующих интерфейсы. (Теоретически. На практике это может не работать так хорошо.)

2
ответ дан 27 November 2019 в 17:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: