Je suis dans une situation difficile. Je travaille dans un référentiel qui fait beaucoup de changements de base de données dans ses fonctions.
La fonction que je traite renvoie des responseids (mais à partir dune transformation, pas dune action de base de données). Cependant, comme effet secondaire, il ajoute un objet contenant ces responseIds à la base de données.
Je devrais donc le nommer:
-
getResponseIds
: Ceci met en évidence les valeurs de retour. Cest une façon de penser très fonctionnelle, mais évidemment si jai la fonctionpostToDB
cela na aucun sens dutilisergetStatusOfPost
-
addResponseIdToDB
: Cela met en évidence leffet secondaire, même si je pense vraiment que beaucoup de mes fonctions ne fonctionnent que sur la base de données (et ont tendance à ne rien renvoyer) -
getAndAddResponseIdsToDB
: Très instructif, mais très long.
Quels sont les avantages et les inconvénients des suggestions ci-dessus? Ou pouvez-vous faire une meilleure suggestion vous-même?
Commentaires
Réponse
Un nom de fonction contenant and
est au niveau dabstraction incorrect .
Je penche vers addResponseIdToDB()
car sinon, l ’« effet secondaire »est une surprise totale. Cependant:
responseIds = addResponseIdToDB();
ne laisse personne surpris.
Le principe de séparation des responsabilités de requête de la commande soutient que cela ne devrait pas être le seul moyen dobtenir lobjet responseId. Il doit également y avoir une requête qui ne modifie pas la base de données qui peut obtenir cet objet.
Contrairement à Bertrand Meyer , je ne crois pas que cela signifie que l’addition doit renvoyer void. Cela signifie simplement qu’une requête pure équivalente doit exister et être facile à trouver afin que la base de données ne soit pas abusée inutilement par lutilisation de requêtes de changement détat.
Étant donné que getResponseIds()
doit exister et ne pas parler à la base de données, le meilleur nom pour une méthode qui fait les deux est en fait addToDB(getResponseId())
. Mais cest juste si vous voulez obtenir toute la composition fonctionnelle à ce sujet.
Commentaires
- En ajoutant à ce qui a été dit, mais en ajoutant un mot qui na pas été ‘ utilisé, je dirais que » et » est approprié lorsquune fonction fait quelque chose qui est atomique . Sil y a un avantage à tirer de deux choses qui se produisent atomiquement / ensemble / à la fois, alors être explicite avec ce que ces deux choses sont dans la fonction nom est bon. Je suis donc d’accord avec votre première affirmation en général, mais en désaccord sans aucune réserve.
- » … ne laisse personne surpris. « . Je ne suis pas daccord, je ‘ suis surpris quune variable booléenne sappelle
responseIds
et je dois ensuite faire un changement mental pour me rendre compte que cela ajoute et obtient tout en un. Mais je ne suis pas daccord avec @tintintong quegetAndAddResponseIdsToDB
est trop long; il ‘ fait environ la moitié de la longueur dun grand nombre de mes noms de fonctions. Si une fonction fait deux choses, dites-le en son nom. - @Luaan OK, quest-ce que vous allez renommer
getAndIncrement
alors?otherIncrement
? - @Luaan Quelle est donc la valeur renvoyée par
Increment
, la valeur pré-incrémentée ou post-incrémentation? Il ‘ nest pas clair ce que ce serait sans plonger dans la documentation.Je pense queincrementAndGet
etgetAndIncrement
sont bien meilleurs les noms de méthodes, ce qui, à mon avis, constitue un bon cas pour » AND » dans les noms de méthodes, au moins dans le cas spécifique des méthodes qui ont des effets secondaires qui pourraient avoir un impact sur la valeur de retour .. - @Luaan Quand vous dites » Incrément renvoie la valeur incrémentée » vous voulez dire la valeur de pré-incrémentation? Gotcha, mal compris au début .. qui est en fait un excellent exemple de pourquoi
getAndIncrement
etincrementAndGet
sont de meilleurs noms que simplementincrement
, même pris individuellement
Answer
Comme dautres lont mentionné, utiliser and
dans un nom de fonction, implique automatiquement quune fonction fait au moins deux choses, ce qui indique généralement quelle fait trop ou fait quelque chose au mauvais endroit.
Lutilisation de la clause and
dans un nom de fonction peut cependant, daprès mon expérience, avoir un sens lorsquil y a des étapes dans un certain flux qui doivent être exécutés en séquence ou lorsque tout le calcul devient significatif.
En calcul numérique, cela peut avoir beaucoup de sens:
normalizeAndInvert(Matrix m)
Je veux dire, qui sait, nest-ce pas? Si vous avez besoin de calculer quelques … disons, des trajectoires déclairage en infographie et à un certa à létape, vous devez normaliser et inverser une certaine matrice, et vous vous retrouvez constamment à écrire:
m = normalize(m)
, suivi de invert(m)
, à titre dexemple simple, présentant labstraction de la normalisation et de linversion, peut être bon du point de vue de la lisibilité.
Parlant sémantiquement, des trucs comme divideAndConquer()
etc, est probablement déconseillé dêtre écrit explicitement, mais, eh bien, le And
est fondamentalement nécessaire.
Commentaires
- Les fonctions font régulièrement plus dune chose, mais ce devraient être de petites choses sajoutant à une grande chose qui ‘ est significatif à un niveau supérieur.
normalizeAndInvert
pourrait êtrecomputeLighting
etdivideAndConquer
pourrait êtreapplyMachiavelli
- Evidemment 🙂 Il suffit de dire que la première création dune fonction avec un » et » dans le nom peut venir comme une abstraction naturelle basée sur un contexte particulier. La refactorisation via la » Méthode de changement de nom » doit venir immédiatement après;)
- @BrunoOliveira Sauf que parfois les deux étapes sont eux-mêmes un composant de beaucoup de choses, renommer nest pas ‘ la bonne approche. Et cela devrait être rare, mais ne jamais lutiliser signifie que vous ‘ vous répétez.
Réponse
Je dirais que cest bien en général, mais ce nest pas acceptable dans tous les cas.
Par exemple, on pourrait sopposer à and
dans un nom de fonction basé sur le principe de responsabilité unique aka le S dans SOLID – car le and
pourrait impliquer responsabilités multiples. Si and
signifie vraiment que la fonction fait deux choses, alors vous voudrez probablement réfléchir attentivement à ce qui se passe.
Un exemple de bibliothèque qui utilise and
dans les noms de fonction peuvent être trouvés dans Java « s concurrence et ici and
est en fait une partie très importante de ce qui se passe et correspond étroitement à ce que vous décrivez dans votre message où l’état est modifié et l’état est renvoyé. Il y a donc clairement des personnes ( et certains cas dutilisation) où cela est jugé acceptable.
Commentaires
- » le
and
pourrait impliquer plusieurs responsabilités « . Et dans ce cas, cela indique effectivement cela. La fonction obtient des ID de réponse quelque part, les écrit dans une base de données et les renvoie. La nécessité de » et » devrait en effet au moins soulever lidée avec lOP que deux fonctions sont nécessaires: une pour obtenir les identifiants et un pour les écrire dans la base de données. - Bien que je convienne que le
and
est une odeur de code, le comportement de base semble légitime: ce nest généralement pas un mauvais idée de renvoyer une ou plusieurs valeurs supplémentaires dune méthode qui savèrent être des résultats (intermédiaires) nécessaires à lexécution de sa fonction principale. Si la fonction principale de cette méthode est dajouter les ID de réponse à la base de données, renvoyer en plus les ID quelle a ajoutés à lappelant est juste une fonctionnalité dutilisation soignée et sans effort de la méthode. - Je ne ‘ ne pense pas que cela viole nécessairement SRP. Vous aurez toujours besoin de fonctions qui font plusieurs choses. Limportant est que ces fonctions appellent une autre fonction pour chaque chose quelles veulent faire au lieu de contenir directement le code requis.
- Si la fonction fait deux choses, le nom doit le refléter.
Réponse
En effet, cest « une question difficile à répondre comme de nombreux commentaires peuvent en témoigner. Les gens semblent avoir des opinions et des conseils contradictoires sur ce qui est un bon nom.
Jaimerais ajouter mes 2 cents à ce fil de 2 mois car je pense que je peux ajouter plus de couleurs à ce qui a déjà été suggéré.
Nommer est un processus
Tout cela me rappelle lexcellent guide en 6 étapes: Nommer en tant que processus .
Un nom de fonction médiocre est surprenant et vous réalisez que vous ne pouvez pas faire confiance. Une bonne dénomination est difficile à obtenir du premier coup. Cela devient plus facile avec lexpérience.
6 étapes itératives pour créer un bon nom
- Remplacer le nom surprenant par un non-sens évident comme
appleSauce()
. Cela semble idiot, mais cest temporaire et il est évident que le nom ne peut pas être fiable. - Obtenir un nom honnête , basé sur ce que vous comprenez de ce que fait la fonction. Dites que vous n’avez pas encore compris l’insertion dans la partie DB,
getResponseIdsAndProbablyUseDb
serait une option. - Obtenir complètement honnête , de sorte que le nom de la fonction indique tout ce que la fonction fait (
getAndAddResponseIdsToDB
ougetResponsesButAlsoAddCacheOfTheResponsesToTheDb
de @Fattie sont dexcellents exemples) - Aller à « fait la bonne chose » qui est fondamentalement la partie où vous divisez votre fonction le long du « ET ». Vous avez donc en fait
getResponseIds
etaddResponseIdsToDb
. - Accédez à un nom « Intention Revealing » qui résout le point de « Je veux toujours obtenir lID de réponse I inséré dans la base de données après lavoir fait « . Arrêtez de penser aux détails dimplémentation et construisez une abstraction qui utilise les 2 plus petites fonctions pour construire autre chose. Cest le niveau dabstraction supérieur mentionné par @candied_orange. Pour e xample
createResponseIds
pourrait le faire et sera la composition degetResponseIds
etaddResponseIdsToDb
. - Accédez à labstraction de votre domaine . Cest difficile, cela dépend de votre entreprise. Il faut du temps pour écouter la langue de votre entreprise. Finalement, vous pourriez vous retrouver avec le concept de
Response
etResponse.createIds()
aurait du sens. Ou peut-être queResponseId
est quelque chose de précieux et que vous auriez une usine pour en créer plusieurs. Linsertion dans DB serait un détail dimplémentation dun référentiel, etc. Oui, la conception serait plus abstraite. Ce serait plus riche et vous permettrait dexprimer davantage. Personne en dehors de votre équipe ne peut vous dire quelle devrait être labstraction de domaine correcte dans votre situation. Cela dépend .
Dans votre cas, vous « avez déjà compris 1 et 2, vous devriez donc probablement au moins passer à 3 (utilisez » AND « ) Mais aller plus loin nest pas seulement une question de dénomination, vous « auriez en fait divisé les responsabilités.
Ainsi, les différentes suggestions ici sont valables
Essentiellement:
- Oui, les noms de fonction surprenants sont terribles et personne ne veut sen occuper
- Oui, « AND » est acceptable dans un nom de fonction car il « vaut mieux quun nom trompeur
- Oui, le niveau d’abstraction est un concept connexe et il importe.
Vous n’avez pas à passer à l’étape 6 tout de suite, c’est bien de rester à étape 2, 3 ou 4 jusquà ce que vous sachiez mieux!
Jespère que cela serait utile pour donner une perspective différente sur ce qui ressemble à des conseils contradictoires. Je pense que tout le monde vise le même objectif ( noms non surprenants) mais arrêtez-vous à différentes étapes, en fonction de leur propre expérience.
Heureux de répondre si vous avez des questions 🙂
Réponse
Votre fonction fait deux choses:
- Obtient un ensemble didentifiants via une transformation et les renvoie à lappelant,
- Écrit cet ensemble didentifiants dans une base de données.
Noubliez pas le principe de responsabilité unique ici: vous devez viser une fonction avoir une responsabilité, pas deux. Vous avez deux responsabilités, donc deux fonctions:
getResponseIds
– Obtient un ensemble didentifiants via une transformation et les renvoie à lappelant
addResponseIdToDB
– Prend un ensemble dID et les écrit dans une base de données.
Et toute la question de savoir comment appeler une fonction unique avec des responsabilités multiples disparaît et la tentation de mettre and
dans les noms des fonctions disparaît également.
En prime, comme la même fonction nest plus responsable de deux actions non liées, getResponseIds
pourrait être déplacée hors de votre code de dépôt (où elle nappartient pas car il « ne fait aucune activité liée à la base de données), dans une classe / module de niveau métier distinct, etc.
Commentaires
- Que ‘ est sûrement vrai, mais vous vous retrouvez à répéter plusieurs fois un extrait de code en utilisant ces deux méthodes SRP, alors vous devriez probablement écrire une méthode triviale pour faire cela pour que vous sèchiez, ne devrait ‘ nest-ce pas?
- @maaartinus, si vous vous retrouvez à appeler ces deux méthodes à de nombreux endroits, alors vous avez probablement un problème avec un manque de cohésion dans votre code. Créer une fonction » DRY » masque alors simplement ce manque de cohésion.
Réponse
Avoir un nom de fonction composite est acceptable, donc le schéma de dénomination que vous utilisez dépend de votre contexte. Il y a des puristes qui vous diront de le casser, mais je dirai le contraire.
Il y a deux raisons dessayer déviter de telles choses:
- Pureté – Une fonction qui fait deux choses devrait être converti en deux fonctions qui font une chose chacune.
- Taille lexicale – un
bigUglyCompositeFunctionName
devient difficile à lire.
Largument de pureté est généralement celui sur lequel se concentre. En tant que vague heuristique générale, la séparation des fonctions est une bonne chose. Cela aide à compartimenter ce qui se passe, ce qui facilite la compréhension. Vous êtes moins susceptible de faire des trucs « intelligents » avec le partage de variables qui conduisent à un couplage entre les deux comportements.
La chose à se demander est donc « dois-je le faire quand même? » Je dis que briser les choses est une vague heuristique, donc vous navez vraiment besoin que dun argument décent pour aller contre cela.
Une raison majeure est quil est avantageux de penser les deux opérations comme une seule. Lexemple final de ceci se trouve dans les opérations atomiques: compare_and_set
. compare_and_set
est une pierre angulaire fondamentale de latomique (qui est un moyen de faire du multithreading sans verrous) qui réussit suffisamment pour que beaucoup soient prêts à le considérer comme le pierre angulaire. Il ya un « et » dans ce nom. Il ya une très bonne raison à cela. Lintérêt de cette opération est quelle effectue la comparaison et le réglage en une seule opération indivisible. Si vous lavez divisée en compare()
et set()
, vous « auriez vaincu toute la raison pour laquelle la fonction existait en premier lieu, car deux compares()
pourrait se produire dos à dos avant les set()
s.
Nous voyons également cela dans les performances. Mon exemple préféré est fastInvSqrt . Il sagit dun célèbre algorithme de Quake qui calcule 1 / sqrt (x). Nous combinons les opérations « inverse » et « racine carrée » car cela donne Ils font une approximation de Newton qui fait les deux opérations en une seule étape (et arrive à le faire en mathématiques entières plutôt quen virgule flottante, ce qui importait à lépoque). Si vous faisiez inverse(sqrt(x))
, ce serait beaucoup plus lent!
Il y a des moments où le résultat est plus clair. Jai écrit plusieurs API impliquant des threads où il faut être extrêmement prudent sur des choses comme les blocages. Je ne voulais pas que mon utilisateur soit au courant des détails de mise en œuvre interne (dautant plus que je pourrais les changer), alors jai écrit lAPI avec quelques fonctions «et» qui évitaient à lutilisateur davoir à tenir un verrou pendant plus dun appel de fonction. Cela signifie quils nont jamais eu besoin de savoir comment je gérais la synchronisation multithread sous le capot. En fait, la plupart de mes utilisateurs ne savaient même pas quils se trouvaient dans un environnement multithread!
Donc, même si la règle générale est de casser les choses, il peut toujours y avoir une raison de les coupler. Par exemple , votre utilisateur peut-il casser votre architecture en lajoutant à une base de données plusieurs fois sans le « get » entre les deux? Si chacune des réponses enregistrées dans la base de données doit avoir un identifiant unique, vous pourriez avoir des problèmes si lutilisateur les faisait volontairement Nilly. De même, voudriez-vous jamais laisser un utilisateur « obtenir » sans enregistrer les résultats dans la base de données? Si la réponse est « oui », divisez-les pour que lutilisateur puisse accéder à la fonction « get ». Cependant, si le la sécurité de votre application est cassée si lutilisateur peut « obtenir » sans que le résultat soit enregistré dans la base de données, vous devriez vraiment garder les fonctions ensemble.
Maintenant, en ce qui concerne les problèmes lexicaux, votre nom de fonction doit décrire ce lutilisateur a besoin de le savoir. Commençons par la partie « get ».Il est assez facile de supprimer le « get » dun nom de fonction, car tous vos exemples afficheront une affectation: int id = addResponseIdToDB()
« . Dans tous les endroits où la fonction est utilisée, vous terminerez documenter le fait que la fonction a renvoyé une valeur.
De même, « ajouter » peut être facultatif. Nous utilisons le terme « effet secondaire » comme un terme englobant, mais il existe différentes nuances de celui-ci. Si les entrées de la base de données ne sont quun journal, il ny a peut-être aucune raison de le mettre en évidence. Nous ne voyons jamais de fonctions comme playMusicAndSendPersonalInformationToOurCorporateServers
. Cest juste playMusic
, et si vous avez de la chance, la documentation peut mentionner quelque chose à propos dune connexion socket sur Internet. Dautre part, si les utilisateurs doivent appeler cette fonction dans le but dajouter des éléments à la base de données, alors « ajouter » est essentiel. Ne le supprimez pas.
Maintenant, jai écrit beaucoup de raisons de faire toutes les combinaisons possibles de ce que vous demandez. Je nai pas répondu intentionnellement à la question car il y a un choix à faire. Ce nest pas une règle à suivre. Vous êtes en train de créer une API.
Cela étant dit, mon instinct est que addResponseIdToDB est probablement la meilleure réponse. Dans la plupart des cas, la partie « obtenir » est un effet secondaire assez évident pour ne pas gagner le bruit supplémentaire causé par la saisie partout. Cependant, il y a quelques endroits où je pourrais le considérer comme important:
- Si le « get » coûte cher – Si vous devez faire un « get » qui implique de récupérer quelque chose sur Internet puis de lajouter à une base de données de cache locale, le « get » est certainement important. Cest la chose que lutilisateur essaie de faire.
- Si vous avez besoin dindiquer clairement que lutilisateur doit faire attention à la valeur. Si lutilisateur souhaite utiliser la variable, il réalisera que lAPI la renvoie et lutilisera. Cependant, vous voulez faire attention à lutilisateur qui ne sait pas quil en a besoin. Par exemple, si vous devez « fermer » cette opération par ID plus tard pour libérer de la mémoire, vous voudrez peut-être attirer lattention sur le fait que vous « fais quelque chose. Dans ces cas, vous voudrez peut-être regarder un verbe autre que « get ». « get » implique souvent des fonctions idempotentes (lappeler à nouveau ne fait rien). Sil renvoie des ressources, dautres verbes comme « create » ou « acquis » sont sympas. Ce mécanisme déchec particulier est un problème majeur en C lors de la gestion des exceptions . C na pas le mécanisme catch / throw de C ++, donc il sappuie sur les codes de retour. Les développeurs sont célèbres pour simplement ne pas avoir vérifié ces codes de retour et se retrouver dans de très mauvaises situations comme des débordements de tampon à cause de cela.
- Symétrie – Parfois, vous concevez une API pour quelle ait une symétrie lexicale. Vous configurez les mots de sorte quils viennent par paires, ou dautres modèles, et créez les commandes de manière à ce quelles soient faciles à identifier visuellement si le modèle a été suivi ou non. Je dirais que cest rare, mais ce nest pas inconnu. Il existe « une raison pour laquelle la fermeture des balises XML répète le nom de la balise (comme < foo > et < / foo >).
Commentaires
- Pour les personnes qui ne ‘ Je connais les mathématiques: 1 / x et sqrt (x) sont des fonctions plutôt lentes. En utilisant des mathématiques intelligentes, nous pouvons calculer 1 / sqrt (x) plus rapidement que soit une division, soit une racine carrée. En fait, tellement plus rapide que le moyen le plus rapide de calculer sqrt (x) est de calculer 1 / sqrt (x) avec une implémentation intelligente, et de multiplier le résultat par x.
- Comme en physique, où les unités élémentaires ne sont plus le mètre et la seconde, mais la seconde et la vitesse de la lumière, car nous pouvons mesurer la vitesse de la lumière avec plus de précision que la longueur dun mètre.
Réponse
Quelle est lultime intention de cette meth od? Pourquoi ajouter ces identifiants transformés à la base de données, est-ce à des fins de mise en cache? Je vais travailler sous cette hypothèse.
Il semble que lintention de la méthode est en fait juste dobtenir les identifiants de réponse transformés (soit en effectuant la transformation, soit en les récupérant à partir dun cache), et ainsi de suite le nom de votre méthode:
getTransformedResponseIds
ou moins verbeusement getResponseIds()
Une méthode portant ce nom pourrait mettre en cache ou peut-être pas, et cela peut être documenté, mais le nom de votre méthode ne devrait pas vous lier à une implémentation spécifique; Que faire si vous décidez darrêter dutiliser une base de données et de les mettre en cache ailleurs, par exemple temporairement en mémoire?
Les effets secondaires devraient être juste que, les effets secondaires, ils devraient probablement être documentés mais ils ne devraient pas vraiment avoir un impact sur lintention principale (ou le succès) ou le nom de la fonction. Si lobtention de la valeur du cache échoue (ou si vous configurez pour ignorer la mise en cache), cela ne devrait pas être un problème critique, la méthode devrait simplement recalculer de manière transparente, mettre en cache (ou non) et renvoyer la nouvelle valeur.
Parfois, lutilisation dun « AND » est utile pour transmettre une intention, comme avec les méthodes Java getAndIncrement
et incrementAndGet
donc ce « nest certainement pas hors de la table.
Réponse
Vous dites que la fonction renvoie quelque chose » dune transformation, pas de any db action « .
Cela suggère que la fonction se comporte plus comme un constructeur quun getter. Cependant, les constructeurs ne devraient pas non plus causer deffets secondaires (merci pour le lien, @ MechMK1 ).
Les méthodes dusine, par contre, présupposent déjà un certain niveau de désordre . Par exemple, lune des motivations de lutilisation des méthodes de fabrique est quune méthode de fabrique:
Permet un code plus lisible dans les cas où plusieurs constructeurs existent, chacun pour une raison différente. – wikipedia
De même,
Si vous avez besoin de travailler sur les effets secondaires, une méthode dusine peut être nommée de manière à ce que ces effets secondaires soient plus clairs. – ruakh
Envisagez de faire de la fonction une méthode dusine, en utilisant le terme «connecté» pour alerter les programmeurs sur le stockage de la base de données:
- Déclaration:
createLoggedResponseid(...) {...} // log object to db
- Appel:
responseid = createLoggedResponseid(...);
Commentaires
- Les constructeurs ne doivent pas causer deffets secondaires
- @ MechMK1 Jai ‘ modifié pour supprimer la suggestion de constructeur , et pour fournir plus de support pour une méthode de fabrique correctement nommée. Faites-moi savoir ce que vous penser.
persistResponseIds
?getStatusOfPost
🙂responseIds = createResponseIds()
semble clair et concis. Vousget()
quelque chose qui nexistait ‘ avant, donccreate
est le verbe approprié. Le truc nouvellement créé est ce que vous ‘ attendez dune fonctioncreate()
à renvoyer. Ou peut-être que ‘ me manque quelque chose dévident?