Qualquer coisa que implemente I3 precisará implementar I1 e I2, e objetos de diferentes classes que herdam I3 podem ser usados de forma intercambiável (veja abaixo), então é correto chamar I3 vazio? Se sim, qual seria a melhor maneira de arquitetar isso?
"Agrupar" I1
e I2
no I3
oferece um bom atalho (?) Ou algum tipo de açúcar de sintaxe para onde você quiser que ambos sejam implementados.
O problema dessa abordagem é que você não pode impedir que outros desenvolvedores implementem explicitamente I1
e I2
lado a lado, mas não funciona nos dois sentidos - embora : I3
seja equivalente a : I1, I2
, : I1, I2
não é equivalente a : I3
. Mesmo se eles forem funcionalmente idênticos, uma classe que ofereça suporte a ambos I1
e I2
não será detectada como suporte I3
.
Isso significa que você está oferecendo duas maneiras de realizar a mesma coisa - "manual" e "doce" (com o açúcar da sintaxe). E isso pode ter um impacto ruim na consistência do código. Oferecendo 2 maneiras diferentes de realizar a mesma coisa aqui, ali e em outro lugar, já resulta em 8 combinações possíveis :)
void foo() {
I1 something = new A();
something = new B();
something.SomeI1Function();
something.SomeI2Function(); // we can't do this without casting first
}
OK, você não pode. Mas talvez seja realmente um bom sinal?
Se I1
e I2
implicar responsabilidades diferentes (caso contrário, não haveria necessidade de separá-las), então talvez foo()
esteja errado tentar fazer something
duas responsabilidades diferentes de uma só vez?
Em outras palavras, pode ser que foo()
ela própria viola o princípio da Responsabilidade Única, e o fato de requerer verificações de tipo de transmissão e de tempo de execução para ser realizado é uma bandeira vermelha que nos preocupa.
EDITAR:
Se você insistiu em garantir que isso foo
requer algo que implementa I1
e I2
, você pode passá-los como parâmetros separados:
void foo(I1 i1, I2 i2)
E eles podem ser o mesmo objeto:
foo(something, something);
O que foo
importa se são da mesma instância ou não?
Mas se isso se importa (por exemplo, porque eles não são apátridas), ou você apenas acha isso feio, pode-se usar genéricos:
void Foo<T>(T something) where T: I1, I2
Agora, restrições genéricas cuidam do efeito desejado sem poluir o mundo exterior com nada. Este é um C # idiomático, baseado em verificações em tempo de compilação, e parece mais correto no geral.
Considero I3 : I2, I1
um esforço para emular tipos de união em uma linguagem que não a suporta imediatamente (C # ou Java não, enquanto os tipos de união existem em linguagens funcionais).
Tentar imitar uma construção que não é realmente suportada pelo idioma geralmente é desajeitado. Da mesma forma, em C #, você pode usar métodos e interfaces de extensão se quiser emular mixins . Possível? Sim. Boa ideia? Não tenho certeza.