Question

Il s’agit en quelque sorte d’une question complémentaire à la présente question .

Supposons que j'ai un arbre d'héritage comme suit:

Car -> Ford -> Mustang -> MustangGT

Est-il avantageux de définir des interfaces pour chacune de ces classes? Exemple:

ICar -> IFord -> IMustang -> IMustangGT

Je vois que d’autres classes (comme Chevy ) souhaiteraient implémenter Icar ou IFord et peut-être même IMustang , mais probablement pas IMustangGT car il est si spécifique. Les interfaces sont-elles superflues dans ce cas?

De plus, je penserais que toute classe souhaitant implémenter IFord voudrait certainement utiliser son héritage unique en héritant de Ford afin de ne pas dupliquer le code. Si tel est le cas, quel est l’avantage de mettre également en œuvre IFord ?

Était-ce utile?

La solution

Selon mon expérience, les interfaces sont mieux utilisées lorsque vous avez plusieurs classes qui doivent chacune répondre à la même méthode ou aux mêmes méthodes, de manière à pouvoir être utilisées de manière interchangeable par un autre code qui sera écrit sur l'interface commune de ces classes. La meilleure utilisation d'une interface est lorsque le protocole est important mais que la logique sous-jacente peut être différente pour chaque classe. Si vous dupliquez autrement la logique, envisagez plutôt les classes abstraites ou l'héritage de classes standard.

Et en réponse à la première partie de votre question, je vous déconseille de créer une interface pour chacune de vos classes. Cela encombrerait inutilement votre structure de classe. Si vous trouvez que vous avez besoin d'une interface, vous pouvez toujours l'ajouter plus tard. J'espère que cela aide!

Adam

Autres conseils

Je suis également d'accord avec la réponse d'Adamalex selon laquelle les interfaces doivent être partagées par les classes auxquelles devrait répondre certaines méthodes.

Si les classes ont des fonctionnalités similaires, mais ne sont pas directement liées les unes aux autres dans une relation ancestrale, une interface constituerait un bon moyen d'ajouter cette fonction aux classes sans dupliquer les fonctionnalités entre les deux. (Ou plusieurs implémentations avec seulement des différences subtiles.)

Alors que nous utilisons une analogie avec une voiture, un exemple concret. Disons que nous avons les classes suivantes:

Car -> Ford   -> Escape  -> EscapeHybrid
Car -> Toyota -> Corolla -> CorollaHybrid

Les voitures ont des roues et peuvent Conduire () et Steer () . Ces méthodes doivent donc exister dans la classe Car . (La classe Car sera probablement une classe abstraite.)

En descendant la ligne, nous obtenons la distinction entre Ford et Toyota (probablement implémenté comme différence dans le type d'emblème sur la voiture, encore probablement une classe abstraite. )

Enfin, nous avons enfin une classe Escape et Corolla , qui sont des classes complètement implémentées en tant que voiture.

Maintenant, comment pourrions-nous fabriquer un véhicule hybride ?

Nous pourrions avoir une sous-classe de Escape qui est EscapeHybrid qui ajoute une méthode FordsHybridDrive () et une sous-classe de Corolla qui est CorollaHybrid avec la méthode ToyotasHybridDrive () . Les méthodes font fondamentalement la même chose, mais nous avons des méthodes différentes. Beurk. On dirait que nous pouvons faire mieux que cela.

Supposons qu'un hybride possède une méthode HybridDrive () . Puisque nous ne voulons pas finir par avoir deux types différents d’hybrides (dans un monde parfait), nous pouvons donc créer une interface IHybrid qui utilise une méthode HybridDrive () . .

Donc, si nous voulons créer une classe EscapeHybrid ou CorollaHybrid , il suffit de mettre en oeuvre la < code> IHybrid interface .

Pour un exemple concret, examinons Java. Une classe capable de comparer un objet à un autre implémente l'interface Comparable . Comme son nom l'indique, l'interface doit être destinée à une classe comparable , d'où le nom "Comparable".

Un exemple de voiture

/ a> est utilisé dans le Interfaces du cours le didacticiel Java .

Vous ne devez implémenter aucune de ces interfaces.

L'héritage de classe décrit ce qu'est un objet est (par exemple, c'est son identité). C'est bien, mais la plupart du temps ce qu'est un objet est , est beaucoup moins important que ce qu'un objet fait . C’est là que les interfaces entrent en jeu.

Une interface doit décrire ce qu'un objet fait ) ou comment il agit. J'entends par là que c'est le comportement et l'ensemble des opérations qui ont un sens étant donné ce comportement.

En tant que tels, les noms d’interface corrects doivent généralement se présenter sous la forme IDrivable , IHasWheels , etc. Parfois, la meilleure façon de décrire ce comportement est de faire référence à un autre objet bien connu. Vous pouvez donc dire "agit comme l'un de ceux-ci". (par exemple: IList ), mais à mon humble avis, cette forme de dénomination est minoritaire.

Etant donné cette logique, les scénarios dans lesquels l'héritage d'interface a un sens sont complètement différents des scénarios dans lesquels l'héritage d'objets a un sens - souvent, ces scénarios n'ont aucun rapport avec eachother du tout.

J'espère que cela vous aidera à réfléchir aux interfaces dont vous auriez besoin: -)

Je dirais seulement faire une interface pour les choses que vous devez faire référence. Vous avez peut-être d'autres classes ou fonctions à connaître sur une voiture, mais combien de fois y aura-t-il quelque chose à connaître sur un gué?

Ne construisez pas des objets inutiles. S'il s'avère que vous avez besoin d'interfaces, c'est un petit effort de les reconstruire.

De plus, du côté pédant, j'espère que vous ne construisez pas réellement quelque chose qui ressemble à cette hiérarchie. Ce n'est pas pour cela que l'héritage doit être utilisé.

Ne le créez qu'une fois ce niveau de fonctionnalité requis.

La refactorisation du code est toujours en cours.

Il existe des outils qui vous permettront d'extraire une interface si nécessaire. PAR EXEMPLE. http://geekswithblogs.net/ JaySmith / archive / 2008/02/27 / refactor-visual-studio-extract-interface.aspx

Créez une ICar et tout le reste (Make = Ford, Model = Mustang et autres) en tant que membres d’une classe qui implémente l’interface.

Vous voudrez peut-être avoir votre classe Ford et, par exemple, la classe GM et les deux implémenter ICar pour pouvoir utiliser le polymorphisme si vous ne voulez pas vérifier Construire == Quel que soit , c'est bon à votre style.

Quoi qu'il en soit - à mon avis, ce sont les attributs d'une voiture et non l'inverse - vous n'avez besoin que d'une interface, car les méthodes sont courantes: Brake, SpeedUp, etc.

Une Ford peut-elle faire des choses que d’autres voitures ne peuvent pas? Je ne pense pas.

Je créerais les deux premiers niveaux, ICar et IFord, et laisserais le deuxième niveau jusqu'à ce que j'ai besoin d'une interface à ce second niveau.

Réfléchissez bien à la manière dont vos objets doivent interagir les uns avec les autres dans votre domaine de problèmes et déterminez si vous devez avoir plusieurs implémentations d'un concept abstrait particulier. Utilisez Interfaces pour créer un contrat autour d'un concept avec lequel d'autres objets interagissent.

Dans votre exemple, je suggérerais que Ford est probablement un fabricant et que Mustang est une valeur ModelName utilisée par le fabricant Ford. Par conséquent, vous pourriez avoir quelque chose de plus semblable à:

IVehichle - > CarImpl, MotorbikeImpl - a-a Constructeur a-a beaucoup NomNom

Dans cette réponse, à propos de la différence entre interface et classe , J’ai expliqué que:

  • interface expose ce qu'est un concept (en termes de "ce que est " valide, au moment de la compilation) et est utilisé pour valeurs (MonInterface x = ...)
  • class expose ce qu'un concept fait (réellement exécuté à l'exécution) et est utilisé pour les valeurs ou pour les objets (MyClass x ou aMyClass.method ())

Donc, si vous devez stocker dans une variable 'Ford' (notion de 'valeur') différentes sous-classes de Ford, créez un IFord. Sinon, ne vous embêtez pas avant d’en avoir réellement besoin.

C’est un critère: s’il n’est pas satisfait, IFord est probablement inutile.
S'il est satisfait, les autres critères exposés dans les réponses précédentes s'appliquent: Si un Ford a une API plus riche qu'une voiture, un IFord est utile à des fins de polymorphismes. Sinon, ICar suffit.

De mon point de vue, les interfaces sont un outil permettant de mettre en œuvre une exigence selon laquelle une classe implémente une certaine signature ou (comme je l’aime à le penser) un certain "Comportement". Pour moi, je pense que si la capitale, au début de mon interface, se nomme comme un pronom personnel et que j'essaie de nommer mes interfaces afin qu'elles puissent être lues de cette façon ... ICanFly, IKnowHowToPersistMyself IAmDisplayable, etc. ... Donc, dans votre exemple Je ne créerais pas d'interface pour reproduire en miroir la signature publique complète d'une classe spécifique. J'analyserais la signature publique (le comportement), puis séparerais les membres en groupes logiques plus petits (comme le plus petit de tous les types), comme (à l'aide de votre exemple), IMove, IUseFuel, ICarryPassengers, ISteerable, IAccelerate, IDepreci, etc., puis appliquez ces interfaces à toutes les autres classes de mon système en ont besoin

En général, la meilleure façon de penser à cela (et à de nombreuses questions de OO) est de penser à la notion de contrat.

Un contrat est défini comme un accord entre deux (ou plusieurs) parties, énonçant des obligations spécifiques que chaque partie doit respecter; dans un programme, il s’agit des services qu’une classe fournira et de ce que vous devez lui fournir pour obtenir les services. Une interface énonce un contrat que toute classe implémentant l'interface doit satisfaire.

Dans cet esprit, votre question dépend toutefois de la langue que vous utilisez et de ce que vous voulez faire.

Après de nombreuses années d’exécution (par exemple, oh mon dieu, 30 ans), j’écrivais généralement une interface pour chaque contrat, en particulier en Java, car cela facilite tellement les tests: si j’ai une interface pour la classe, Je peux construire facilement des objets fantaisie, presque trivialement.

Les interfaces sont destinées à être une API publique générique et les utilisateurs ne pourront utiliser cette API publique. Si vous ne souhaitez pas que les utilisateurs utilisent les méthodes spécifiques à IMustangGT , vous pouvez limiter la hiérarchie d'interface à ICar et IExpensiveCar .

Hériter uniquement des interfaces et des classes abstraites.

Si vous avez deux classes presque identiques et que vous devez implémenter la majorité des méthodes, utilisez et Interface en combinaison avec l'achat de l'autre objet.
Si les classes Mustang sont si différentes, créez non seulement une interface ICar, mais également IMustang.
Ainsi, les classes Ford et Mustang peuvent hériter de ICar, et Mustang et MustangGT de ICar et IMustang.

Si vous implémentez la classe Ford et qu'une méthode est identique à Mustang, achetez chez Mustang:

class Ford{
  public function Foo(){
    ...
    Mustang mustang  = new Mustang();
    return mustang.Foo();
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top