Все, что реализует I3, должно будет реализовывать I1 и I2, и объекты из разных классов, которые наследуют I3, могут затем использоваться взаимозаменяемо (см. Ниже), поэтому правильно ли называть I3 пустым? Если так, что было бы лучшим способом для архитектуры этого?
«Bundling» I1и I2into I3предлагает хороший (?) Ярлык или какой-то синтаксический сахар для того, где бы вы ни хотели реализовать оба.
Проблема с этим подходом состоит в том, что вы не можете помешать другим разработчикам явным образом реализовать I1 и I2 бок о бок, но он не работает в обоих направлениях - хотя : I3является эквивалентом : I1, I2, а : I1, I2не эквивалентом : I3. Даже если они функционально идентичны, класс, который поддерживает оба I1и I2не будет определен как поддерживающий I3.
Это означает, что вы предлагаете два способа достижения одного и того же - «ручной» и «сладкий» (с вашим синтаксическим сахаром). И это может плохо повлиять на целостность кода. Предлагая 2 разных способа выполнить одно и то же здесь, там и в другом месте, мы получаем уже 8 возможных комбинаций :)
void foo() {
I1 something = new A();
something = new B();
something.SomeI1Function();
something.SomeI2Function(); // we can't do this without casting first
}
ОК, ты не можешь. Но, может быть, это действительно хороший знак?
Если I1и I2подразумевать разные обязанности (иначе не было бы необходимости разделять их в первую очередь), то, может быть foo(), неправильно пытаться somethingвыполнять две разные обязанности за один раз?
Другими словами, это может быть то, что foo()само по себе нарушает принцип Единой Ответственности, и тот факт, что для его проверки требуются проверки типов приведения и выполнения, является красным сигналом тревоги, который нас об этом беспокоит.
РЕДАКТИРОВАТЬ:
Если вы настаивали на том, чтобы что- fooто реализовывало, I1и I2вы могли бы передать их как отдельные параметры:
void foo(I1 i1, I2 i2)
И они могут быть одним и тем же объектом:
foo(something, something);
Какая fooразница, если они один и тот же экземпляр или нет?
Но если это все равно (например, потому что они не являются лицами без гражданства), или вы просто думаете, что это выглядит ужасно, можно вместо этого использовать дженерики:
void Foo<T>(T something) where T: I1, I2
Теперь общие ограничения заботятся о желаемом эффекте, не загрязняя внешний мир ничем. Это идиоматический C #, основанный на проверках во время компиляции, и в целом он кажется более правильным.
Я воспринимаю это I3 : I2, I1как попытку эмулировать типы объединения в языке, который не поддерживает его «из коробки» (C # или Java этого не делают, тогда как типы объединения существуют в функциональных языках).
Попытка эмулировать конструкцию, которая не поддерживается языком, часто неуклюжа. Точно так же в C # вы можете использовать методы расширения и интерфейсы, если вы хотите эмулировать миксины . Возможно? Да. Хорошая идея? Я не уверен.