J'ai un collègue assis à côté de moi qui a conçu une interface comme celle-ci:
public interface IEventGetter {
public List<FooType> getFooList(String fooName, Date start, Date end)
throws Exception;
....
}
Le problème est que, pour le moment, nous n'utilisons pas ce paramètre "end" dans notre code, mais simplement parce que nous aurons peut-être besoin de l'utiliser ultérieurement.
Nous essayons de le convaincre que c’est une mauvaise idée d’introduire des paramètres dans des interfaces qui ne sont d'aucune utilité pour le moment, mais il continue d'insister sur le fait que beaucoup de travail devra être fait si nous implémentons l'utilisation de la "date de fin" quelque temps plus tard et doivent adapter tout le code alors.
Maintenant, ma question est la suivante: existe-t-il des sources traitant d'un sujet comme celui des gourous du code "respectés", vers lesquelles nous pouvons le relier?
end
paramètre à cet objet et même le modifier par défaut afin de ne pas rompre le codenull
. Les classes d'implémentation peuvent alors remplacer si nécessaire.IQueryable
(on ne peut prendre que certaines expressions) à du code en dehors de la DALRéponses:
Invitez-le à en savoir plus sur YAGNI . La partie "justification" de la page Wikipedia peut être particulièrement intéressante ici:
Autres arguments possibles:
«80% du coût à vie d'un logiciel est destiné à la maintenance» . Écrire du code juste à temps réduit le coût de la maintenance: il faut maintenir moins de code et se concentrer sur le code réellement nécessaire.
Le code source est écrit une fois, mais lu des dizaines de fois. Un argument supplémentaire, non utilisé nulle part, conduirait à une perte de temps en termes de compréhension du pourquoi d’un argument qui n’est pas nécessaire. Étant donné qu'il s'agit d'une interface avec plusieurs implémentations possibles, les choses ne sont que plus difficiles.
Le code source devrait être auto-documenté. La signature elle-même est trompeuse, car le lecteur penserait que cela
end
affecte le résultat ou l'exécution de la méthode.Les personnes écrivant des implémentations concrètes de cette interface peuvent ne pas comprendre que le dernier argument ne devrait pas être utilisé, ce qui conduirait à des approches différentes:
Je n'ai pas besoin
end
, alors je vais simplement ignorer sa valeur,Je n'ai pas besoin
end
, alors je vais lancer une exception si ce n'est pas le casnull
,Je n'ai pas besoin
end
, mais je vais essayer de l'utiliser,J'écrirai beaucoup de code qui pourrait être utilisé plus tard, quand ce
end
sera nécessaire.Mais sachez que votre collègue a peut-être raison.
Tous les points précédents sont basés sur le fait que la refactorisation est facile, donc ajouter un argument plus tard ne nécessitera pas beaucoup d'effort. Mais il s’agit d’une interface. Elle peut être utilisée par plusieurs équipes contribuant à d’autres parties de votre produit. Cela signifie que changer une interface peut être particulièrement pénible, auquel cas, YAGNI ne s'applique pas vraiment ici.
La réponse de hjk donne une bonne solution: ajouter une méthode à une interface déjà utilisée n’est pas particulièrement difficile, mais dans certains cas, cela a aussi un coût substantiel:
Certains frameworks ne supportent pas les surcharges. Par exemple, et si je me souviens bien (corrigez-moi si je me trompe), le WCF de .NET ne prend pas en charge les surcharges.
Si l'interface comporte de nombreuses implémentations concrètes, l'ajout d'une méthode à l'interface nécessiterait de passer en revue toutes les implémentations et d'ajouter la méthode également.
la source
(un peu plus tard)
C'est tout ce dont vous avez besoin, la surcharge de méthodes. La méthode supplémentaire à l'avenir peut être introduite de manière transparente sans affecter les appels à la méthode existante.
la source
end
paramètre est inutile maintenant et a continué à utiliser laend
méthode -less tout au long du projet, la mise à jour de l'interface est le moindre de leurs soucis car il devient nécessaire de mettre à jour le classes d'implémentation. Ma réponse vise spécifiquement la partie "adapter tout le code", car il semblerait que le collègue pensait devoir mettre à jour les appelants de la méthode d'origine tout au long du projet pour introduire un troisième paramètre default / dummy. .Les appels à l'autorité ne sont pas particulièrement convaincants; préférable de présenter un argument qui est valable, peu importe qui l'a dit.
Voici une reductio ad absurdum qui devrait persuader ou démontrer que votre collègue est bloqué pour avoir "raison" indépendamment de sa sensibilité:
Ce dont tu as vraiment besoin c'est
Vous ne savez jamais quand vous devrez internationaliser pour Klingon. Vous devez donc vous en occuper maintenant, car il faudra beaucoup de travail pour le mettre à niveau et les Klingons ne sont pas réputés pour leur patience.
la source
You never know when you'll have to internationalize for Klingon
. C'est pourquoi vous devriez simplement passer unCalendar
, et laisser le code client décider s'il veut envoyer unGregorianCalendar
ou unKlingonCalendar
(je parie que certains l'ont déjà fait). Duh. :-pStarDate sdStart
etStarDate sdEnd
? Nous ne pouvons pas oublier la Fédération après tout ...Date
!HebrewDate start
etHebrewDate end
.Du point de vue du génie logiciel, je pense que la solution appropriée à ce type de problèmes réside dans le modèle de constructeur. C'est certainement un lien des auteurs de 'gourou' pour votre collègue http://en.wikipedia.org/wiki/Builder_pattern .
Dans le modèle de générateur, l'utilisateur crée un objet contenant les paramètres. Ce conteneur de paramètres sera ensuite passé à la méthode. Cela évitera toute extension et surcharge des paramètres dont votre collègue aurait besoin à l'avenir tout en rendant le tout très stable lorsque des changements doivent être apportés.
Votre exemple deviendra:
la source
IEventGetter
àISearchFootRequest
. Je suggérerais plutôt quelque chose commepublic IFooFilter
avec membrepublic bool include(FooType item)
qui retourne s'il faut inclure l'élément. Ensuite, les implémentations d’interfaces individuelles peuvent décider de la manière dont elles filtrerontVous n'en avez pas besoin maintenant, alors ne l'ajoutez pas. Si vous en avez besoin plus tard, étendez l'interface:
la source
Aucun principe de conception n'est absolu. Par conséquent, bien que je sois généralement d'accord avec les autres réponses, je pensais que je deviendrais l'avocat du diable et discuterais de certaines conditions dans lesquelles j'accepterais d'accepter la solution de votre collègue:
Now()
, l'ajout d'un paramètre supprime un effet secondaire, ce qui présente des avantages pour la mise en cache et les tests unitaires. Peut-être que cela permet une implémentation plus simple. Ou peut-être est-il plus cohérent avec les autres API de votre code.Cela étant dit, selon mon expérience, le plus grand corollaire de YAGNI est YDKWFYN: Vous ne savez pas de quelle forme vous aurez besoin (oui, je viens de créer cet acronyme). Même si le besoin d' un paramètre limite peut être relativement prévisible, il peut prendre la forme d'une limite de pages, d'un nombre de jours ou d'un booléen spécifiant l'utilisation de la date de fin à partir d'un tableau de préférences utilisateur, ou un nombre important d'éléments.
Étant donné que vous n'avez pas encore l'exigence, vous n'avez aucun moyen de savoir quel type doit être ce paramètre. Vous vous retrouvez souvent avec une interface délicate qui ne correspond pas tout à fait à votre exigence, ou devant la modifier de toute façon.
la source
Il n'y a pas assez d'informations pour répondre à cette question. Cela dépend de ce que
getFooList
fait réellement et de la façon dont il le fait.Voici un exemple évident de méthode qui devrait supporter un paramètre supplémentaire, utilisé ou non.
Implémenter une méthode qui opère sur une collection où vous pouvez spécifier le début mais pas la fin de la collection est souvent ridicule.
Il faut vraiment examiner le problème lui-même et se demander si le paramètre est absurde dans le contexte de toute l'interface et quel est le fardeau imposé par le paramètre supplémentaire.
la source
J'ai bien peur que votre collègue ait un argument très valable. Bien que sa solution ne soit en réalité pas la meilleure.
De son interface proposée, il est clair que
renvoie des instances trouvées dans un intervalle de temps. Si les clients n'utilisent pas actuellement le paramètre end, cela ne change rien au fait qu'ils s'attendent à des instances trouvées dans un intervalle de temps. Cela signifie simplement que tous les clients utilisent actuellement des intervalles ouverts (du début à la fin).
Donc, une meilleure interface serait:
Si vous fournissez intervalle avec une méthode fabrique statique:
alors les clients ne ressentent même plus le besoin de spécifier une heure de fin.
Dans le même temps, votre interface indique plus correctement ce qu’elle fait, car
getFooList(String, Date)
elle ne communique pas qu’elle traite d’un intervalle.Notez que ma suggestion découle de ce que la méthode fait actuellement, et non de ce qu'elle devrait ou pourrait faire à l'avenir, et en tant que tel, le principe de YAGNI (qui est en effet très valable) ne s'applique pas ici.
la source
Ajouter un paramètre inutilisé est déroutant. Les gens peuvent appeler cette méthode en supposant que cette fonctionnalité fonctionnera.
Je ne l'ajouterais pas. Il est trivial de l'ajouter plus tard en utilisant une refactorisation et en réparant mécaniquement les sites d'appels. Dans un langage typé statiquement, cela est facile à faire. Pas nécessaire de l'ajouter avec empressement.
S'il est une raison d'avoir ce paramètre , vous pouvez éviter la confusion: Ajouter une assertion dans la mise en œuvre de cette interface pour faire respecter que ce paramètre soit passé comme la valeur par défaut. Si quelqu'un utilise accidentellement ce paramètre au moins, il remarquera immédiatement lors des tests que cette fonctionnalité n'est pas implémentée. Cela supprime le risque de glisser un bogue dans la production.
la source