Alles, was I3 implementiert, muss I1 und I2 implementieren, und Objekte aus verschiedenen Klassen, die I3 erben, können dann austauschbar verwendet werden (siehe unten). Ist es also richtig, I3 leer aufzurufen? Wenn ja, was wäre ein besserer Weg, dies zu architektonisieren?
"Bundling" I1
und " I2
into" I3
bieten eine nette (?) Verknüpfung oder eine Art Syntaxzucker für alle Anwendungen, für die Sie beide implementieren möchten.
Das Problem bei diesem Ansatz ist , dass Sie nicht andere Entwickler von explizit Implementierung stoppen können I1
und I2
nebeneinander, aber es funktioniert nicht in beide Richtungen - während : I3
entspricht der : I1, I2
, : I1, I2
ist kein Äquivalent : I3
. Auch wenn sie funktional identisch sind, eine Klasse, die beide unterstützt I1
und I2
nicht als unterstützend erkannt wird I3
.
Dies bedeutet, dass Sie zwei Möglichkeiten anbieten, um dasselbe Ziel zu erreichen: "manuell" und "süß" (mit Ihrem Syntaxzucker). Dies kann sich negativ auf die Codekonsistenz auswirken. Das Anbieten von 2 verschiedenen Möglichkeiten, um das Gleiche hier, dort und an einer anderen Stelle zu erreichen, führt bereits zu 8 möglichen Kombinationen :)
void foo() {
I1 something = new A();
something = new B();
something.SomeI1Function();
something.SomeI2Function(); // we can't do this without casting first
}
OK, das kannst du nicht. Aber vielleicht ist es tatsächlich ein gutes Zeichen?
Wenn I1
und I2
implizieren unterschiedliche Verantwortlichkeiten (ansonsten wäre es gar nicht nötig, sie zu trennen), dann ist es vielleicht foo()
falsch something
, zwei unterschiedliche Verantwortlichkeiten auf einmal ausführen zu lassen?
Mit anderen Worten, es könnte sein, dass dies foo()
selbst gegen das Prinzip der Einzelverantwortung verstößt, und die Tatsache, dass Casting und Laufzeit-Typprüfungen erforderlich sind, ist eine rote Fahne, die uns alarmiert.
BEARBEITEN:
Wenn Sie darauf bestehen möchten, foo
dass für die Implementierung von I1
und Folgendes erforderlich ist I2
, können Sie diese als separate Parameter übergeben:
void foo(I1 i1, I2 i2)
Und sie könnten dasselbe Objekt sein:
foo(something, something);
Was foo
kümmert es, ob sie dieselbe Instanz sind oder nicht?
Aber wenn es Ihnen etwas ausmacht (zB weil sie nicht staatenlos sind), oder wenn Sie einfach denken, dass dies hässlich aussieht, könnten Sie stattdessen Generika verwenden:
void Foo<T>(T something) where T: I1, I2
Jetzt sorgen allgemeine Beschränkungen für den gewünschten Effekt, ohne die Außenwelt mit irgendetwas zu verschmutzen. Dies ist ein idiomatisches C #, das auf Prüfungen zur Kompilierungszeit basiert und sich insgesamt besser anfühlt.
Ich sehe I3 : I2, I1
etwas als Versuch zu emulieren Union - Typ in einer Sprache , die sie nicht von der Box unterstützt aus (C # oder Java nicht, während Union - Typen in funktionalen Sprachen existieren).
Der Versuch, ein Konstrukt zu emulieren, das von der Sprache nicht wirklich unterstützt wird, ist oft umständlich. Ebenso können Sie in C # Erweiterungsmethoden und Schnittstellen verwenden, wenn Sie Mixins emulieren möchten . Möglich? Ja. Gute Idee? Ich bin mir nicht sicher.