Est-ce une mauvaise conception POO pour une simulation impliquant des interfaces?

13

Je conçois mon propre petit programme OOP pour simuler les vampires, les loups, les humains et les camions et j'essaie de mettre en œuvre ma propre compréhension limitée des interfaces.

( Je suis encore en train de résumer ici et je n'ai pas encore d'implémentation de code, donc c'est plutôt une question de conception OOP ... Je pense!)

Ai-je raison de rechercher un «comportement commun» entre ces classes et de les implémenter en tant qu'interfaces ?

Par exemple, les vampires et les loups mordent ... alors devrais-je avoir une interface de morsure?

public class Vampire : Villain, IBite, IMove, IAttack

De même pour les camions ...

public class Truck : Vehicle, IMove

Et pour les humains ...

public class Man : Human, IMove, IDead

Est-ce que je pense ici? (Apprécier ton aide)

user3396486
la source
14
Les animaux, les légumes et les minéraux font rarement de bons exemples de mise en œuvre d'applications. Implémentations actuelles sont généralement plus abstraites, comme IEnumerable, IEquatable, etc.
Robert Harvey
6
Vous avez une seule mention de ce que vos objets sont sur le point de faire dans votre logiciel ("bite"). Le logiciel est normalement conçu pour faire quelque chose, baser un modèle objet sur des caractéristiques uniquement ne mène nulle part.
tofro
@tofro Mon intention était qu'IBite contienne plusieurs méthodes qui implémenteraient un comportement concernant (1) La réduction du niveau de vie / d'énergie d'un autre (2) L'apparition ou l'invocation de graphiques `` sanguins '' et (3) la mise à jour des statiques de simulation données (telles que NoOfBites). Je pense que je peux comprendre qu'une interface est mieux utilisée pour implémenter une gamme de comportements de méthode.
user3396486
2
Les classes Humain, Vampire et Véhicule n'implémentent-elles pas déjà l'interface IMove? Pourquoi avez-vous besoin que les sous-classes l'implémentent trop explicitement?
Pierre Arlaud
Toutes ces interfaces sont-elles vraiment nécessaires? En Python, heureusement, vous n'avez besoin de rien de tout cela, ce qui a été un changement vraiment rafraîchissant (mon premier langage était Object Pascal). Les méthodes virtuelles peuvent également être une meilleure solution dans certains cas.
Ajasja

Réponses:

33

En général, vous voulez avoir des interfaces pour les caractéristiques communes de votre classe.

Je suis à moitié d'accord avec @Robert Harvey dans les commentaires, qui a dit que les interfaces représentent généralement des caractéristiques plus abstraites des classes. Néanmoins, je trouve à partir d'exemples plus concrets une bonne façon de commencer à penser abstrait.

Bien que votre exemple soit techniquement correct (c'est-à-dire oui, les vampires et les loups mordent, vous pouvez donc avoir une interface pour cela), il y a une question de pertinence. Chaque objet a des milliers de caractéristiques (par exemple, les animaux peuvent avoir de la fourrure, nager, grimper aux arbres, etc.). Allez-vous créer une interface pour chacun d'eux? Très peu probable.

Vous voulez généralement que les interfaces pour les choses qui ont du sens soient regroupées dans une application dans son ensemble. Par exemple, si vous construisez un jeu, vous pouvez avoir un tableau d'objets IMove et mettre à jour leur position. Si vous ne voulez pas faire cela, avoir l'interface IMove est assez inutile.

Le fait est, ne faites pas trop d'ingénieur. Vous devez penser à comment allez-vous utiliser cette interface, et 2 classes ayant une méthode en commun ne sont pas une raison suffisante pour créer une interface.

Paul92
la source
1
J'espère certainement que chaque objet n'a pas des milliers d'attributs.
gardenhead
4
Attributs non pas comme dans les attributs oop mais attributs / caractéristiques de grammaire (choses telles que énumérables, comparables, etc.): D. Mauvais choix de mots.
Paul92
3
Il convient de noter que les interfaces utiles sont celles que vous utiliserez. Par exemple, ce IBiten'est pas particulièrement utile, mais vous voudrez peut-être que IAttackvous puissiez travailler sur toutes les choses qui font des attaques, ou IUpdateque vous puissiez exécuter les mises à jour pour tout, ou IPhysicsEnabledque vous puissiez leur appliquer de la physique, etc.
anaximander
1
Cette réponse soulève de très bons points. Le dernier paragraphe le résume assez bien; au moins aussi bien que possible avec le niveau de détail fourni.
Courses de légèreté avec Monica
1
Le regroupement des méthodes communes convient mieux aux classes abstraites. Les interfaces sont faites pour concevoir des contrats qui doivent respecter ceux qui l'implémentent, ne regroupant pas la même implémentation pour certains objets.
Walfrat
28

On dirait que vous créez un tas d' interfaces de méthode unique . C'est très bien à première vue, mais gardez à l'esprit que les interfaces n'appartiennent pas aux classes qui les implémentent. Ils appartiennent aux clients qui les utilisent. Les clients décident si quelque chose doit être quelque chose qui peut bouger et attaquer.

Si j'ai une Combatclasse avec une fight()méthode, cette méthode a probablement besoin d'appeler les deux move()et attack()sur le même objet. Cela suggère fortement la nécessité d'une ICombatantinterface fight()pouvant appeler move()et attack()passer. C'est plus propre que de fight()prendre un IAttackobjet et de le lancer IMovepour voir s'il peut également bouger.

Cela ne signifie pas que vous ne pouvez pas également avoir d' IMove IAttackinterfaces. J'espère juste que vous ne les faites pas sans qu'un client en ait besoin. Inversement, si aucun client n'a besoin de faire bouger et d'attaquer un objet,ICombatant n'est pas nécessaire.

Cette façon simple de regarder les interfaces est souvent perdue car les gens aiment les exemples suivants. Les premières interfaces auxquelles nous sommes exposés se trouvent dans les bibliothèques. Malheureusement, les bibliothèques n'ont aucune idée de ce que sont leurs clients. Ils ne peuvent donc que deviner les besoins de leurs clients. Pas le meilleur exemple à suivre.

candied_orange
la source
1
Merde, c'est bon. Le jeu semble être un très bon moyen d'utiliser et d'expliquer la POO.
JeffO
6
@JeffO jusqu'à ce que vous implémentiez réellement un jeu assez grand et que vous réalisiez que la POO est un gâchis brûlant et que vous feriez mieux avec des systèmes basés sur des composants ou des conceptions orientées données.
Darkhogg
"Les interfaces appartiennent aux clients qui les utilisent"
Tibos
1
+1 pour la différence entre les bibliothèques et les applications, je lis souvent (trop?: /) Des tonnes de choses qui conviennent à l'une et non à l'autre.
Walfrat
3

Demandez-vous s'il sera courant d'avoir des collections d'objets avec différentes combinaisons de capacités, et si le code peut vouloir exécuter une action sur les éléments, dans une collection, qui le prennent en charge . Si c'est le cas, et s'il y aurait un "comportement par défaut" sensible pour les objets qui n'ont pas de support utile pour une action, il peut être utile d'avoir des interfaces implémentées par un large éventail de classes, pas seulement celles qui peuvent se comporter utilement.

Par exemple, supposons que seuls quelques types de créatures peuvent avoir des Woozles, et on veut que ces créatures aient une NumerOfWoozlespropriété. Si une telle propriété se trouvait dans une interface qui n'était implémentée que par des créatures pouvant avoir des Woozles, alors le code qui voulait trouver le nombre total de Woozles détenus par une collection de créatures de types mixtes devrait dire quelque chose comme:

int total = 0;
foreach (object it in creatures)
{
   IWoozleCountable w = trycast(it, IWoozleCountable);
   if (w != null) total += w.WoozleCount;
}

Si, cependant, WoozleCount était membre de Creature / ICreature, même si peu de sous-types remplaceraient l'implémentation par défaut de WoozleCount de Creature qui renvoie toujours zéro, le code pourrait être simplifié pour:

int total = 0;
foreach (ICreature it in creatures)
   total += it.WoozleCount;

Bien que certaines personnes puissent s'irriter à l'idée que chaque créature implémente une propriété WoozleCount qui n'est vraiment utile que pour quelques sous-types, la propriété aurait un sens pour tous les types, qu'elle soit utile ou non avec des éléments connus pour être de ces types, et je considérerais l'interface "évier de cuisine" comme étant moins une odeur de code que l'opérateur trycast.

supercat
la source