Cualquier cosa que implemente I3 necesitará implementar I1 e I2, y los objetos de diferentes clases que heredan I3 se pueden usar indistintamente (ver más abajo), ¿es correcto llamar a I3 vacío? Si es así, ¿cuál sería una mejor manera de arquitectura esto?
"Bundling" I1
y I2
into I3
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
y I2
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 ambos I1
y I2
no se detectará como compatible I3
.
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 :)
void foo() {
I1 something = new A();
something = new B();
something.SomeI1Function();
something.SomeI2Function(); // we can't do this without casting first
}
OK, no puedes ¿Pero tal vez es realmente una buena señal?
Si I1
e I2
implica diferentes responsabilidades (de lo contrario, no habría necesidad de separarlas en primer lugar), entonces ¿tal vez foo()
es incorrecto intentar something
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 implemente I1
y I2
, podría pasarlos como parámetros separados:
void foo(I1 i1, I2 i2)
Y podrían ser el mismo objeto:
foo(something, something);
¿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:
void Foo<T>(T something) where T: I1, I2
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.