Comment éviter d'écrire de nombreuses fonctions d'intercommunication dans un wrapper?


13

J'ai une classe, qui encapsule une autre classe d'un type de base commun. Étant donné que l'interface de type de base est assez grande, cela implique d'écrire de nombreuses fonctions d'intercommunication. Je cherche un moyen d'éviter cela.

Faisons un exemple:

      Car
     /   \
Volvo     VolvoWithTrailer

Maintenant, je dois implémenter chaque fonction dans l'interface de voiture pour VolvoWithTrailer et appeler la fonction appropriée sur l'objet Volvo enveloppé, sauf peut-être GetMaxSpeed ​​() qui renverra quelque chose de plus bas. Fondamentalement, j'aurai de nombreuses fonctions comme

int VolvoWithTrailer::GetNumSeats() {
  return mVolvo.GetNumSeats()
}

Un moyen évident de résoudre ce problème serait de faire de VolvoWithTrailer une sous-classe de Volvo

    Car
     |
   Volvo
     |
VolvoWithTrailer

mais cela semble violer le principe de privilégier la composition à l'héritage.

Sinon, comment pourrais-je éviter d'écrire tous ces wrappers (le langage est C ++)? Ou - si c'est votre position - pourquoi devrais-je simplement les écrire / utiliser l'héritage? Y a-t-il une magie de modèle qui pourrait aider à cela?


2
Je n'ai pas trop réfléchi à cela et je pense à Java. Et une interface 'Trailer'? VolvoWithTrailer étendrait la Volvo et implémenterait l'interface de remorque?

7
Privilégiez la composition à l'héritage uniquement lorsque cela a du sens.
Robert Harvey

@RobertHarvey: +1. C'est une ligne directrice, pas un «principe» et certainement pas un commandement absolu.
Mason Wheeler

En Python, vous pouvez résoudre ce problème avec l'introspection (ce que C # appelle la réflexion).
user16764

1
Votre IDE ne gère-t-il pas la génération de code?
Amy Blankenship

Réponses:


5

Mon opinion est que vous devez écrire tout ce que vous devez écrire pour faire du long terme stabilité et la maintenabilité à du programme. Quel est l'intérêt d'éviter d'écrire 20 lignes de code aujourd'hui, si chaque fois que vous touchez quelque chose qui utilise ce code ou revenez pour maintenir cette classe, cela vous coûte 5 minutes supplémentaires ou plus parce que vous n'avez pas mis le temps aujourd'hui?

La question devient alors "qu'est-ce que tu as besoin d'écrire?" Je ne pense pas que vous fournissez suffisamment d'informations pour être en mesure de donner une réponse définitive, alors je vais simplement énumérer certaines choses auxquelles je penserais pour prendre une décision:

1. Avez-vous besoin d'avoir l'objet Trailer seul, maintenant ou dans un avenir prévisible?
Si c'est le cas, vous avez un assez bon argument pour créer le wrapper autour des deux objets composites, car vous devrez déjà envelopper l'un ou l'autre. Autant être cohérent.

2. L'un des trois types (Car, Trailer, CarWithTrailer) se trouve-t-il dans une zone du code que les développeurs consultent fréquemment ou qui semblent le devenir?
Dans l'affirmative, soyez très prudent, car les ramifications du choix de la mauvaise chose peuvent être très coûteuses amorties à chaque fois que le code est touché. Sinon, cela ne changera peut-être rien à ce que vous décidez. Choisissez simplement une direction et suivez-la.

3. Qu'est-ce qui est le plus facile à comprendre?
Est-ce qu'une approche vous saute aux yeux que quelqu'un venant derrière vous "obtiendrait" immédiatement ce que vous essayez de faire? Vos coéquipiers ont-ils des préjugés particuliers qui pourraient rendre une approche plus difficile à maintenir? Si toutes choses sont égales, choisissez la solution qui impose la charge cognitive la plus faible.

4. Y a-t-il des avantages supplémentaires à utiliser une approche par rapport à une autre?
Une chose qui me saute aux yeux est que l'idée du wrapper a un avantage distinct si vous l'écrivez que votre CarWithTrailerWrapper peut envelopper n'importe quelle voiture et n'importe quelle remorque dans un CarWithTrailer. Ensuite, vous finissez par écrire toutes vos méthodes d'intercommunication, mais vous ne les écrivez qu'une seule fois , plutôt qu'une fois pour chaque classe de voiture.

Cela rend votre investissement initial dans la rédaction des méthodes de transmission beaucoup moins cher lorsque vous ajoutez plus de voitures et de remorques. Cela aide également à réduire la tentation de coupler trop directement les choses au Volvo ou au VolvoWithTrailer, tout en réduisant les conséquences du couplage au CarWithTrailerWrapper, car vous pouvez faire beaucoup plus avec cette seule implémentation.

Il y a peut-être des avantages distincts que vous obtiendriez gratuitement en utilisant la méthode d'extension, mais je ne les vois pas.

OK, il semble donc que je me sois engagé à aller de l'avant et à remplir les méthodes d'intercommunication, pour les applications non triviales.

Je ne suis pas un programmeur C ++, donc je n'ai aucune idée des outils de génération de code à votre disposition. L'IDE que j'utilise peut générer des méthodes à partir d'une interface et je peux ajouter des modèles de code qui rendraient l'écriture pass-through assez indolore. Si vous n'avez pas accès à un IDE qui le fait, vous pouvez réellement obtenir assez loin en laissant Excel écrire du code pour vous .


3

Étant donné que l'interface de type de base est assez grande, cela implique d'écrire de nombreuses fonctions d'intercommunication.

Et il y a votre problème.

Les interfaces ne doivent gérer qu'une seule responsabilité. Les garder fins et concentrés limite la quantité de travail de relais que vous pourriez avoir à faire dans le cas d'un décorateur, d'un proxy ou d'un autre wrapper (en plus de rendre le simpilateur de classe à utiliser, tester, maintenir et étendre).


5
Quoi? Une classe peut implémenter plusieurs interfaces et les interfaces peuvent hériter les unes des autres. Donc, même si une interface est assez petite, cela n'a rien à voir avec le fait que les classes qui les implémentent auront besoin de beaucoup de fonctions ou de fonctions d'intercommunication. Plus les classes sont petites et bien définies , plus il est probable que vous aurez besoin de fonctions d'intercommunication.
Amy Blankenship
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.