Все, что реализует I3, должно будет реализовывать I1 и I2, и объекты из разных классов, которые наследуют I3, могут затем использоваться взаимозаменяемо (см. Ниже), поэтому правильно ли называть I3 пустым? Если так, что было бы лучшим способом для архитектуры этого?
«Bundling» I1
и I2
into 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 # вы можете использовать методы расширения и интерфейсы, если вы хотите эмулировать миксины . Возможно? Да. Хорошая идея? Я не уверен.