Насколько я могу судить, пустые интерфейсы, как правило, считают плохой практикой, особенно когда такие вещи, как атрибуты, поддерживаются языком.
Однако считается ли интерфейс «пустым», если он наследуется от других интерфейсов?
interface I1 { ... }
interface I2 { ... } //unrelated to I1
interface I3
: I1, I2
{
// empty body
}
Все, что реализует I3
, потребуется для реализации I1
и I2
, и объекты из разных классов, которые наследуют, I3
могут затем использоваться взаимозаменяемо (см. Ниже), так ли это правильно называть I3
пустым ? Если так, что было бы лучшим способом для архитектуры этого?
// with I3 interface
class A : I3 { ... }
class B : I3 { ... }
class Test {
void foo() {
I3 something = new A();
something = new B();
something.SomeI1Function();
something.SomeI2Function();
}
}
// without I3 interface
class A : I1, I2 { ... }
class B : I1, I2 { ... }
class Test {
void foo() {
I1 something = new A();
something = new B();
something.SomeI1Function();
something.SomeI2Function(); // we can't do this without casting first
}
}
foo
? (Если ответ «да», тогда вперед!)var : I1, I2 something = new A();
локальным переменным, был бы хорош (если мы игнорируем хорошие моменты, поднятые в ответе Конрада)Ответы:
«Bundling»
I1
иI2
intoI3
предлагает хороший (?) Ярлык или какой-то синтаксический сахар для того, где бы вы ни хотели реализовать оба.Проблема с этим подходом состоит в том, что вы не можете помешать другим разработчикам явным образом реализовать
I1
иI2
бок о бок, но он не работает в обоих направлениях - хотя: I3
является эквивалентом: I1, I2
, а: I1, I2
не эквивалентом: I3
. Даже если они функционально идентичны, класс, который поддерживает обаI1
иI2
не будет определен как поддерживающийI3
.Это означает, что вы предлагаете два способа достижения одного и того же - «ручной» и «сладкий» (с вашим синтаксическим сахаром). И это может плохо повлиять на целостность кода. Предлагая 2 разных способа выполнить одно и то же здесь, там и в другом месте, мы получаем уже 8 возможных комбинаций :)
ОК, ты не можешь. Но, может быть, это действительно хороший знак?
Если
I1
иI2
подразумевать разные обязанности (иначе не было бы необходимости разделять их в первую очередь), то, может бытьfoo()
, неправильно пытатьсяsomething
выполнять две разные обязанности за один раз?Другими словами, это может быть то, что
foo()
само по себе нарушает принцип Единой Ответственности, и тот факт, что для его проверки требуются проверки типов приведения и выполнения, является красным сигналом тревоги, который нас об этом беспокоит.РЕДАКТИРОВАТЬ:
Если вы настаивали на том, чтобы что-
foo
то реализовывало,I1
иI2
вы могли бы передать их как отдельные параметры:И они могут быть одним и тем же объектом:
Какая
foo
разница, если они один и тот же экземпляр или нет?Но если это все равно (например, потому что они не являются лицами без гражданства), или вы просто думаете, что это выглядит ужасно, можно вместо этого использовать дженерики:
Теперь общие ограничения заботятся о желаемом эффекте, не загрязняя внешний мир ничем. Это идиоматический C #, основанный на проверках во время компиляции, и в целом он кажется более правильным.
Я воспринимаю это
I3 : I2, I1
как попытку эмулировать типы объединения в языке, который не поддерживает его «из коробки» (C # или Java этого не делают, тогда как типы объединения существуют в функциональных языках).Попытка эмулировать конструкцию, которая не поддерживается языком, часто неуклюжа. Точно так же в C # вы можете использовать методы расширения и интерфейсы, если вы хотите эмулировать миксины . Возможно? Да. Хорошая идея? Я не уверен.
источник
Если бы особая важность была в том, чтобы класс реализовывал оба интерфейса, то я не вижу ничего плохого в создании третьего интерфейса, который наследуется от обоих. Тем не менее, я был бы осторожен, чтобы не попасть в ловушку сверхинженерных вещей. Класс может наследовать от одного или другого, или обоих. Если у моей программы не было большого количества классов в этих трех случаях, было бы немного сложно создать интерфейс для обоих, и уж точно, если бы эти два интерфейса не имели никакого отношения друг к другу (пожалуйста, никаких интерфейсов IComparableAndSerializable).
Напоминаю, что вы также можете создать абстрактный класс, который наследуется от обоих интерфейсов, и вы можете использовать этот абстрактный класс вместо I3. Я думаю, что было бы неправильно говорить, что вы не должны этого делать, но у вас должна быть хотя бы веская причина.
источник
Я не согласен. Читайте о маркерных интерфейсах .
источник
instanceof
вместо полиморфизма