Comment implémenter uniquement une partie d'une interface

14

Lors du développement en POO, une interface / contrat est parfois fourni par une bibliothèque que vous ne pouvez pas modifier. Appelons cette interface J.

Vous avez maintenant un objet de classe A qui consomme des objets qui implémentent cette interface. A l'intérieur A, seule une petite partie des définitions de l'interface est nécessaire. Certaines des classes d'objets sont créées par moi pendant le projet (appelons l'une d'elles de type D), il y a donc un surcoût dans l'implémentation de tout à l'intérieur de l'interface J.

Je veux implémenter un sous-ensemble des fonctionnalités dans l'interface J, mais mes solutions jusqu'à présent ne me satisfont pas:

  • implémenter tous les aspects de J et ensuite lancer "notImplementedExceptions" désinforme l'utilisateur de mes objets: il semblerait que mes objets de type D soient conformes à l'interface J, mais ils ne le font pas - et d'autres consommateurs de mes objets (qui acceptent les objets implémentant l'interface J) ne peut pas compter sur l'intégrité de mes objets.
  • L'implémentation d'une interface nouvellement définie m'interdit d'utiliser des objets qui implémentent uniquement l'interface J, bien que l'interface J soit entièrement compatible avec ma propre interface.
  • Laisser mes objets personnalisés implémenter l'interface J créerait une surcharge importante, car ils n'ont pas besoin de toutes ces fonctionnalités.

Lorsque je pouvais modifier l'interface J, je créais une "super-interface" K qui avait ce sous-ensemble de la fonctionnalité de l'interface J, et je faisais hériter l'interface J de l'interface K. Mais je ne peux pas altérer l'interface J.

Qu'est-ce qu'une solution orientée objet à ce problème? La meilleure solution est-elle toujours de mettre en œuvre l'interface "juste" J? Ou existe-t-il des moyens OOP pour "superclasser" une interface sans la modifier?

vstrien
la source
1
Découvrez comment les classes * Adapter de Swing procèdent ainsi.

Réponses:

9

si vous ne contrôlez pas l'interface J, vous êtes coincé.

vous pouvez, pour plus de clarté, implémenter votre propre interface subJ et interface J, et utiliser subJ dans votre propre code pour indiquer clairement que les méthodes supplémentaires dans l'interface J ne sont pas nécessaires, mais je ne pense pas que cela vous gagne vraiment beaucoup

si possible, implémentez complètement l'interface J complète

si possible, contactez le propriétaire de l'interface J et demandez-lui de la modifier pour mieux l'adapter à vos besoins

Steven A. Lowe
la source
2
Comme compromis: vous pouvez demander au propriétaire de l'interface J d'hériter de votre plus petite interface SubJ.
k3b
27

L'intérêt de la mise en œuvre d'une interface est de fournir un contrat ferme entre l'appelant et l'appelé qui ne change jamais alors que les détails de la mise en œuvre peuvent varier.

En implémentant l'interface J, vous dites au monde extérieur ce que vous pouvez faire.

Si vous n'avez pas besoin de la moitié de ce que fait J, alors l'interface doit vraiment être subdivisée car elle n'est pas aussi petite qu'elle pourrait l'être, ou vous devez utiliser NotImplementedExceptionles méthodes et les propriétés dont vous n'avez pas besoin. Cependant, ce n'est pas la meilleure solution car elle confond les attentes des gens quant à ce que votre code peut faire.

ChrisF
la source
5

Si votre classe de consommateurs A ne nécessite pas l'intégralité de l'interface J, je suggère de créer une nouvelle interface (appelez-la K), qui décrit de manière exhaustive tout ce dont elle a besoin.

Cela donne un signal clair à toute personne utilisant la classe A quant à son côté du contrat. Améliorant ainsi les chances de réutilisation. Si quelqu'un a besoin de fournir un objet qui implémente une interface large et complexe, afin de faire quelque chose de relativement simple, il finira probablement par écrire ce que la classe A fait lui-même.

Afin de permettre à la classe A de consommer des objets qui implémentent uniquement l'interface J, vous pouvez fournir une classe wrapper qui implémente l'interface K et transmet tous les appels appropriés à un membre de l'interface J.

Paul Butcher
la source
3

Bien que je sois d'accord avec les réponses existantes qui disent que vous devez vraiment implémenter J complètement (avec des exceptions pour les méthodes non implémentées) ou pas du tout, une solution de contournement possible est la suivante:

  • Créez une interface plus petite K qui est un sous-ensemble de J et implémente les méthodes requises.
  • Créez un wrapper pour A qui accepte les objets qui implémentent J et K.
  • Dans le cas d'un passage en J, il suffit de le pousser jusqu'à l'instance de A.
  • Dans le cas d'un passage en K, instanciez une implémentation anonyme de J, ne câblant que les méthodes de K à J qui s'y trouvent. Passez le résultat à l'instance de A.

Veuillez noter que c'est une solution assez laide, car elle nécessite un wrapper qui accepte plusieurs choses qui sont essentiellement la même chose. Mais il atteint les objectifs suivants:

  • Aucun changement à J ou A.
  • Aucune implémentation "exposée" de J incomplète.

Si votre langage OO ne permet pas l'instanciation anonyme de l'interface, vous pouvez créer une implémentation factice de l'interface et l'instancier.

Deckard
la source
1

Combien de temps la mise en œuvre de l'interface entière vous prendrait-elle?

Si cela vous prendrait beaucoup de temps, cela indique un problème de conception, et je vous conseillerais (comme Steven A. l'a fait avant moi) de contacter le propriétaire et de voir s'il peut être changé à l'avenir.

Utilisez-vous l'interface dans votre propre code, indépendamment de la bibliothèque de l'interface?

Comme l'a suggéré Steven A., vous pouvez utiliser votre propre interface comme vous le souhaitez dans votre propre code. Cela maintient au moins votre code interne propre. Vous pouvez également l'envoyer au propriétaire en tant qu'interface que vous attendez de trouver. Peut-être qu'il est d'accord avec vous, ou peut-être qu'il peut vous expliquer pourquoi l'interface ne devrait pas être divisée.

Votre implémentation aurait-elle jamais besoin des membres inutilisés de l'interface?

Dans le cas où vous pouvez vous attendre à ce qu'ils ne soient jamais appelés car ils ne sont pas pris en charge, je préfère utiliser le NotSupportedException. Cela donne une indication claire que vous ne supporterez jamais cette interface, plutôt que vous ne l'avez pas implémentée.

Steven Jeuris
la source
Une bonne idée sur son utilisation NotSupportedExceptionindique clairement que vous ne supportez pas cette méthode.
ChrisF
@ChrisF: J'utilise uniquement NotImplementedExceptionpour le code de production, où j'écrirais autrement "TODO: Implémentez ceci" Malheureusement, ils n'apparaissent pas dans la liste des tâches de Visual Studio . Mais dans la pratique, je laisse à peine une méthode non mise en œuvre pendant plus de deux jours. Resharper affiche également ces exceptions en gras (au moins avec ma configuration). :)
Steven Jeuris
0

Implémentez ce dont vous avez besoin et lancez une exception NoImplementedException sur les autres.

C'est une question d'utilisation. Si vous n'utilisez pas l'interface, ou si vous savez que votre code n'utilisera pas l'interface, alors ne consacrez pas de temps à cela, car vous n'avez pas besoin d'investir du temps sur du code redondant.

Travaillez pour la tâche à accomplir et gardez une bonne trace pour les autres s'ils souhaitent utiliser l'interface.

De nombreuses interfaces en Java ne sont pas pleinement implémentées.

C'est l'approche Apache des choses: http://commons.apache.org/lang/api-2.4/org/apache/commons/lang/NotImplementedException.html

Afficher un nom
la source
Expliquez peut-être pourquoi vous croyez que c'est une bonne solution. Cela n'ajoute rien à la question.
Steven Jeuris
il n'est pas nécessaire de tout mettre en œuvre, car il n'y a pas vraiment besoin de tout expliquer. si tel était son cas, certaines parties de l'interface étaient redondantes, il le prendrait immédiatement.
Nom d'affichage