Les interfaces vides sont généralement considérées comme une mauvaise pratique, pour autant que je sache - en particulier lorsque des éléments tels que les attributs sont pris en charge par le langage.
Cependant, une interface est-elle considérée comme «vide» si elle hérite d'autres interfaces?
interface I1 { ... }
interface I2 { ... } //unrelated to I1
interface I3
: I1, I2
{
// empty body
}
Tout ce qui implémente I3
devra implémenter I1
and I2
, et les objets de différentes classes qui héritent I3
peuvent alors être utilisés de manière interchangeable (voir ci-dessous), est-il donc juste d'appeler I3
empty ? Si tel est le cas, quelle serait la meilleure façon d’architecturer cela?
// 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 réponse est "oui", alors allez-y!)var : I1, I2 something = new A();
pour les variables locales serait bien (si nous ignorons les bons points soulevés dans la réponse de Konrad)Réponses:
"Bundling"
I1
etI2
intoI3
offre un bon raccourci (?), Ou une sorte de sucre de syntaxe pour l'endroit où vous souhaitez que les deux soient implémentés.Le problème avec cette approche est que vous ne pouvez pas empêcher d'autres développeurs d'implémenter explicitement
I1
etI2
côte à côte, mais cela ne fonctionne pas dans les deux sens - alors qu'il: I3
est équivalent à: I1, I2
,: I1, I2
n'est pas un équivalent de: I3
. Même si elles sont fonctionnellement identiques, une classe qui prend en charge les deuxI1
etI2
ne sera pas détectée comme prenant en chargeI3
.Cela signifie que vous offrez deux façons d'accomplir la même chose - "manuelle" et "douce" (avec votre sucre de syntaxe). Et cela peut avoir un mauvais impact sur la cohérence du code. Offrir 2 façons différentes d'accomplir la même chose ici, là et ailleurs entraîne déjà 8 combinaisons possibles :)
OK, tu ne peux pas. Mais c'est peut-être en fait un bon signe?
Si
I1
etI2
impliquent des responsabilités différentes (sinon il ne serait pas nécessaire de les séparer en premier lieu), alors peutfoo()
- être qu'il est faux d'essayer de fairesomething
exécuter deux responsabilités différentes en une seule fois?En d'autres termes, il se pourrait que
foo()
lui - même viole le principe de la responsabilité unique, et le fait qu'il nécessite des vérifications de type de coulée et d'exécution pour le retirer est un drapeau rouge nous alarmant à ce sujet.ÉDITER:
Si vous insistez pour vous assurer que
foo
prend quelque chose qui implémenteI1
etI2
, vous pouvez les passer en tant que paramètres distincts:Et ils pourraient être le même objet:
Qu'importe
foo
si c'est la même instance ou non?Mais si cela se soucie (par exemple parce qu'ils ne sont pas apatrides), ou si vous pensez simplement que cela semble moche, on pourrait utiliser des génériques à la place:
Désormais, les contraintes génériques prennent soin de l'effet recherché sans polluer le monde extérieur avec quoi que ce soit. Il s'agit d'un langage C # idiomatique, basé sur des vérifications à la compilation, et il semble globalement plus correct.
Je vois
I3 : I2, I1
un peu comme un effort pour émuler des types d'union dans un langage qui ne le prend pas en charge (C # ou Java non, alors que les types d'union existent dans les langages fonctionnels).Essayer d'imiter une construction qui n'est pas vraiment prise en charge par le langage est souvent maladroit. De même, en C #, vous pouvez utiliser des méthodes d'extension et des interfaces si vous souhaitez émuler des mixins . Possible? Oui. Bonne idée? Je ne suis pas sûr.
la source
S'il était particulièrement important qu'une classe implémente les deux interfaces, je ne vois rien de mal à créer une troisième interface qui hérite des deux. Cependant, je ferais attention de ne pas tomber dans le piège de la suringénierie. Une classe peut hériter de l'un ou de l'autre, ou des deux. À moins que mon programme n'ait eu beaucoup de classes dans ces trois cas, c'est un peu trop pour créer une interface pour les deux, et certainement pas si ces deux interfaces n'avaient aucune relation l'une avec l'autre (pas d'interfaces IComparableAndSerializable s'il vous plaît).
Je vous rappelle que vous pouvez également créer une classe abstraite qui hérite des deux interfaces, et vous pouvez utiliser cette classe abstraite à la place de I3. Je pense qu'il serait faux de dire que vous ne devriez pas le faire, mais vous devriez au moins avoir une bonne raison.
la source
Je ne suis pas d'accord. Lisez à propos des interfaces de marqueur .
la source
instanceof
au lieu du polymorphisme