Las interfaces vacías generalmente se consideran una mala práctica, por lo que puedo decir, especialmente donde el lenguaje admite cosas como los atributos.
Sin embargo, ¿se considera una interfaz 'vacía' si hereda de otras interfaces?
interface I1 { ... }
interface I2 { ... } //unrelated to I1
interface I3
: I1, I2
{
// empty body
}
Todo lo que implemente I3
deberá implementarse I1
y I2
, y los objetos de diferentes clases que hereden I3
se pueden usar indistintamente (ver más abajo), entonces, ¿es correcto llamar a I3
vacío ? Si es así, ¿cuál sería una mejor manera de arquitectura esto?
// 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
? (Si la respuesta es "sí", ¡adelante!)var : I1, I2 something = new A();
para las variables locales sería buena (si ignoramos los buenos puntos planteados en la respuesta de Konrad)Respuestas:
"Bundling"
I1
yI2
intoI3
ofrece un buen acceso directo (?), O algún tipo de azúcar de sintaxis para donde quiera que se implementen ambos.El problema con este enfoque es que no puede evitar que otros desarrolladores implementen de manera explícita
I1
yI2
en paralelo, pero no funciona en ambos sentidos, aunque: I3
es equivalente a: I1, I2
,: I1, I2
no es equivalente a: I3
. Incluso si son funcionalmente idénticos, una clase que admite ambosI1
yI2
no se detectará como compatibleI3
.Esto significa que está ofreciendo dos formas de lograr lo mismo: "manual" y "dulce" (con su sintaxis de azúcar). Y puede tener un impacto negativo en la consistencia del código. Ofrecer 2 formas diferentes de lograr lo mismo aquí, allá y en otro lugar da como resultado 8 combinaciones posibles ya :)
OK, no puedes ¿Pero tal vez es realmente una buena señal?
Si
I1
eI2
implica diferentes responsabilidades (de lo contrario, no habría necesidad de separarlas en primer lugar), entonces ¿tal vezfoo()
es incorrecto intentarsomething
realizar dos responsabilidades diferentes de una sola vez?En otras palabras, podría ser que en
foo()
sí mismo viola el principio de Responsabilidad Única, y el hecho de que requiera una verificación de tipo de lanzamiento y tiempo de ejecución para llevarlo a cabo es una señal de alerta que nos alarma.EDITAR:
Si insistió en asegurarse de que
foo
toma algo que se implementeI1
yI2
, podría pasarlos como parámetros separados:Y podrían ser el mismo objeto:
¿Qué
foo
importa si son la misma instancia o no?Pero si le importa (por ejemplo, porque no son apátridas), o simplemente cree que esto se ve feo, uno podría usar genéricos en su lugar:
Ahora las restricciones genéricas se encargan del efecto deseado sin contaminar el mundo exterior con nada. Este es C # idiomático, basado en comprobaciones en tiempo de compilación, y se siente más correcto en general.
Veo
I3 : I2, I1
algo como un esfuerzo por emular tipos de unión en un lenguaje que no lo admite de forma inmediata (C # o Java no, mientras que los tipos de unión existen en lenguajes funcionales).Tratar de emular una construcción que no es realmente compatible con el lenguaje a menudo es torpe. Del mismo modo, en C # puede usar métodos de extensión e interfaces si desea emular mixins . ¿Posible? Si. ¿Buena idea? No estoy seguro.
fuente
Si hubiera una importancia particular para que una clase implementara ambas interfaces, entonces no veo nada de malo en crear una tercera interfaz que herede de ambas. Sin embargo, tendría cuidado de no caer en la trampa de las cosas de ingeniería excesiva. Una clase podría heredar de uno u otro, o de ambos. A menos que mi programa tenga muchas clases en estos tres casos, es un poco demasiado crear una interfaz para ambos, y ciertamente no si esas dos interfaces no tienen relación entre sí (no hay interfaces IComparableAndSerializable, por favor).
Le recuerdo que también puede crear una clase abstracta que herede de ambas interfaces, y puede usar esa clase abstracta en lugar de I3. Creo que sería un error decir que no deberías hacerlo, pero al menos deberías tener una buena razón.
fuente
Estoy en desacuerdo. Lea sobre las interfaces de marcadores .
fuente
instanceof
lugar del polimorfismo