As interfaces vazias são geralmente consideradas práticas inadequadas, até onde eu sei - especialmente quando coisas como atributos são suportadas pelo idioma.
No entanto, uma interface é considerada 'vazia' se herdar de outras interfaces?
interface I1 { ... }
interface I2 { ... } //unrelated to I1
interface I3
: I1, I2
{
// empty body
}
Qualquer coisa que implementa I3
precisará implementar I1
e I2
, e objetos de diferentes classes que herdam I3
podem então ser usados alternadamente (veja abaixo), então é certo para chamar I3
vazio ? Se sim, qual seria a melhor maneira de arquitetar isso?
// 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
? (Se a resposta for "sim", então vá em frente!)var : I1, I2 something = new A();
por variáveis locais seria bom (se ignorarmos os pontos bons levantadas na resposta de Konrad)Respostas:
"Agrupar"
I1
eI2
noI3
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
eI2
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 ambosI1
eI2
não será detectada como suporteI3
.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 :)
OK, você não pode. Mas talvez seja realmente um bom sinal?
Se
I1
eI2
implicar responsabilidades diferentes (caso contrário, não haveria necessidade de separá-las), então talvezfoo()
esteja errado tentar fazersomething
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 implementaI1
eI2
, você pode passá-los como parâmetros separados:E eles podem ser o mesmo objeto:
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:
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.
fonte
Se havia uma importância particular em ter uma classe implementando ambas as interfaces, não vejo nada de errado em criar uma terceira interface que herda de ambas. No entanto, eu tomaria cuidado para não cair na armadilha das coisas de excesso de engenharia. Uma classe pode herdar de uma ou outra, ou de ambas. A menos que meu programa tenha muitas classes nesses três casos, é um pouco demais criar uma interface para ambos, e certamente não se essas duas interfaces não tiverem relação uma com a outra (sem interfaces IComparableAndSerializable, por favor).
Lembro que você também pode criar uma classe abstrata que herda de ambas as interfaces e pode usar essa classe abstrata no lugar do I3. Eu acho que seria errado dizer que você não deve fazê-lo, mas pelo menos deve ter um bom motivo.
fonte
Discordo. Leia sobre interfaces de marcador .
fonte
instanceof
em vez de polimorfismo