Alors que certaines directives indiquent que vous devriez utiliser une interface lorsque vous souhaitez définir un contrat pour une classe où l'héritage n'est pas clair (IDomesticated
) et l'héritage lorsque la classe est une extension d'une autre (Chat : Mammifère
, Serpent : Reptile
), il y a des cas où (à mon avis) ces directives entrent dans une zone grise.
Par exemple, disons que mon implémentation était Chat : Animal de compagnie
. Animal de compagnie
est une classe abstraite. Faut-il l'étendre à Chat : Mammifère, IDomesticated
où Mammifère
est une classe abstraite et IDomesticated
est une interface? Ou suis-je en conflit avec les principes KISS/YAGNI (même si je ne suis pas sûr s'il y aura une classe Loup
à l'avenir, qui ne pourrait pas hériter de Animal de compagnie
)?
En m'éloignant des Chat
s métaphoriques et des Animal de compagnie
, disons que j'ai des classes qui représentent des sources de données entrantes. Elles doivent toutes implémenter la même base de quelque manière. Je pourrais mettre en œuvre du code générique dans une classe abstraite Source
et en hériter. Je pourrais aussi simplement créer une interface ISource
(ce qui me semble plus "juste") et réimplémenter le code générique dans chaque classe (ce qui est moins intuitif). Enfin, je pourrais "avoir le beurre et l'argent du beurre" en créant à la fois la classe abstraite et l'interface. Quel est le meilleur choix?
Ces deux cas soulèvent des points pour n'utiliser qu'une classe abstraite, qu'une interface et utiliser à la fois une classe abstraite et une interface. Ces choix sont-ils tous valides, ou existe-t-il des "règles" pour savoir quand utiliser l'un plutôt que l'autre?
J'aimerais préciser que par "utiliser à la fois une classe abstraite et une interface", cela inclut le cas où elles représentent essentiellement la même chose (Source
et ISource
Il convient également de noter que cette question concerne principalement les langues qui ne prennent pas en charge l'héritage multiple (comme .NET et Java).