Например:
Скажем , у меня есть классы A
, B
, C
. У меня есть два интерфейса, давайте называть их IAnimal
и IDog
. IDog
наследует от IAnimal
. A
и B
есть IDog
, а C
нет, но это IAnimal
.
Важной частью является то, что не IDog
предоставляет никаких дополнительных функций. Он используется только для разрешения A
и B
, но не C
для передачи в качестве аргумента определенным методам.
Это плохая практика?
object-oriented
interfaces
Кендалл Фрей
источник
источник
IAnimal
иIDog
являются страшными именами тавтологии!MyProject.Data.IAnimal
иMyProject.Data.Animal
лучше чемMyProject.Data.Interfaces.Animal
иMyProject.Data.Implementations.Animal
Interface
илиImplementation
, или в повторяющемся префиксе, или в пространстве имен, это тавтология в любом случае и нарушает DRY.Dog
это все, что вам нужно заботитьсяPitBull extends Dog
тоже не нужна избыточность реализации, словоextends
говорит мне все, что мне нужно знать, прочитайте ссылку, которую я разместил в своем исходном комментарии.Ответы:
Интерфейс, который не имеет ни одного члена или имеет точно такие же члены с другим интерфейсом, который унаследован от того же интерфейса, называемого интерфейсом маркера, и вы используете его в качестве маркера.
Это неплохая практика, но интерфейс можно заменить атрибутами (аннотациями), если язык, который вы используете, поддерживает это.
Я могу с уверенностью сказать, что это неплохая практика, потому что я видел интенсивное использование шаблона «Маркерный интерфейс» в Orchard Project.
Вот пример из проекта Orchard.
Пожалуйста, обратитесь к интерфейсу маркера .
источник
C
метод, ожидающийIDog
.Kennel
класс, который хранит списокIDog
с.Да, это плохая практика почти на каждом языке.
Типы не существуют для кодирования данных; они помогают доказать, что ваши данные идут туда, куда им нужно, и ведут себя так, как они должны себя вести. Единственная причина для подтипа - изменение полиморфного поведения (и не всегда).
Поскольку здесь нет возможности печатать, подразумевается то, что может сделать IDog. Один программист думает одно, другой думает другое, и все рушится. Или вещи, которые он представляет, увеличиваются, поскольку вам нужен более точный контроль.
[править: уточнение]
Я имею в виду, что вы делаете некоторую логику на основе интерфейса. Почему некоторые методы могут работать только с собаками, а другие - с животными? Эта разница является подразумеваемым договором, поскольку нет фактического члена, который обеспечивает разницу; нет фактических данных в системе. Единственное отличие - это интерфейс (отсюда и комментарий «не печатать»), и то, что это значит, будет отличаться у разных программистов. [/редактировать]
Некоторые языки предоставляют механизмы черт, где это не так уж и плохо, но они, как правило, фокусируются на возможностях, а не на совершенствовании. У меня мало опыта с ними, поэтому я не могу говорить о лучших практиках там. В C ++ / Java / C # хотя ... не хорошо.
источник
Kennel
класс, в котором хранится списокIDog
s.Я хорошо знаю только C #, так что я не могу сказать это для OO-программирования в целом, но в качестве примера на C #, если вам нужно классифицировать классы, очевидными вариантами являются интерфейсы, свойство enum или пользовательские атрибуты. Обычно я выбираю интерфейс независимо от того, что думают другие.
источник
Нет ничего плохого в том, чтобы иметь интерфейс, который наследует другой интерфейс, без добавления новых членов, если ожидается, что все реализации последнего интерфейса будут в состоянии с пользой делать то, чего не могут делать некоторые реализации первого. Действительно, это хороший шаблон, который IMHO должен был бы больше использовать в таких вещах, как .net Framework, особенно потому, что разработчик, чей класс естественным образом удовлетворял бы ограничениям, вытекающим из более производного интерфейса, может указать на это, просто реализовав более производный интерфейс. без необходимости изменения какого-либо кода реализации члена или объявлений.
Например, предположим, у меня есть интерфейсы:
Ожидается, что реализация
IImmutableFoo.AsImmutable()
вернется сама;IMaybeMutableFoo.AsImmutable()
ожидается, что реализация изменяемого класса вернет новый глубоко неизменяемый объект, содержащий моментальный снимок данных. Подпрограмма, которая хочет сохранить снимок данных, хранящихся в,IMaybeMutableFoo
может предложить перегрузки:В тех случаях, когда компилятор не знает, что нужно сохранить
IImmutableFoo
, он будет вызыватьAsImmutable()
. Функция должна быстро вернуться, если элемент уже неизменен, но вызов функции через интерфейс все еще занимает время. Если компилятор знает, что элемент уже естьIImmutableFoo
, он может пропустить ненужный вызов функции.источник