Le chaînage de méthodes est la pratique des méthodes objet renvoyant l'objet lui-même afin que le résultat soit appelé pour une autre méthode. Comme ça:
participant.addSchedule(events[1]).addSchedule(events[2]).setStatus('attending').save()
Cela semble être considéré comme une bonne pratique, car cela produit du code lisible, ou une "interface fluide". Cependant, pour moi, cela semble au contraire casser la notation d'appel d'objet impliquée par l'orientation de l'objet elle-même - le code résultant ne représente pas l'exécution d'actions par rapport au résultat d'une méthode précédente, ce qui est généralement le mode de fonctionnement du code orienté objet:
participant.getSchedule('monday').saveTo('monnday.file')
Cette différence parvient à créer deux significations différentes pour la notation par points de "appeler l'objet résultant": Dans le contexte du chaînage, l'exemple ci-dessus se lirait comme une sauvegarde de l' objet participant , même si l'exemple est en fait destiné à enregistrer le planning objet reçu par getSchedule.
Je comprends que la différence ici est de savoir si la méthode appelée doit être attendue pour retourner quelque chose ou non (auquel cas elle renverrait l'objet appelé lui-même pour le chaînage). Mais ces deux cas ne se distinguent pas de la notation elle-même, seulement de la sémantique des méthodes appelées. Lorsque le chaînage de méthode n'est pas utilisé, je peux toujours savoir qu'un appel de méthode opère sur quelque chose lié au résultat de l'appel précédent - avec le chaînage, cette hypothèse se rompt, et je dois traiter sémantiquement toute la chaîne pour comprendre ce qu'est l'objet réel appelé est vraiment. Par exemple:
participant.attend(event).setNotifications('silent').getSocialStream('twitter').postStatus('Joining '+event.name).follow(event.getSocialId('twitter'))
Là, les deux derniers appels de méthode se réfèrent au résultat de getSocialStream, alors que les précédents font référence au participant. C'est peut-être une mauvaise pratique d'écrire des chaînes où le contexte change (n'est-ce pas?), Mais même dans ce cas, vous devrez constamment vérifier si les chaînes de points qui se ressemblent sont en fait conservées dans le même contexte, ou ne fonctionnent que sur le résultat .
Pour moi, il semble que si le chaînage de méthodes produit superficiellement du code lisible, surcharger le sens de la notation par points ne fait qu'engendrer plus de confusion. Comme je ne me considère pas comme un gourou de la programmation, je suppose que la faute est la mienne. Alors: qu'est-ce que je manque? Est-ce que je comprends mal le chaînage des méthodes? Y a-t-il des cas où le chaînage de méthodes est particulièrement bon, ou d'autres où il est particulièrement mauvais?
Note de bas de page: Je comprends que cette question puisse être lue comme une déclaration d'opinion masquée comme une question. Cependant, ce n'est pas - je veux vraiment comprendre pourquoi le chaînage est considéré comme une bonne pratique, et où est-ce que je me trompe en pensant qu'il rompt la notation orientée objet inhérente.
la source
.
qui ignorerait les valeurs de retour de mehtod et invoquerait toujours les méthodes chaînées utilisant le même objet.Réponses:
Je reconnais que c'est subjectif. Pour la plupart, j'évite le chaînage de méthodes, mais récemment, j'ai également trouvé un cas où c'était juste la bonne chose - j'avais une méthode qui acceptait quelque chose comme 10 paramètres, et en avait besoin de plus, mais pour la plupart du temps, vous n'aviez qu'à spécifier un peu. Avec les dérogations, cela est devenu très fastidieux très rapidement. Au lieu de cela, j'ai opté pour l'approche de chaînage:
L'approche de chaînage de méthodes était facultative, mais elle facilitait l'écriture de code (en particulier avec IntelliSense). Rappelez-vous qu'il s'agit d'un cas isolé et qu'il ne s'agit pas d'une pratique générale dans mon code.
Le fait est que dans 99% des cas, vous pouvez probablement faire aussi bien, voire mieux, sans chaînage de méthodes. Mais il y a le 1% où c'est la meilleure approche.
la source
P = MyObject.GetParamsObj().SomeParameter(asdasd).SomeOtherParameter(asdasd); Obj = MyObject.Start(); MyObject.Execute(P);
. Vous avez l'avantage de pouvoir réutiliser cet objet paramètre dans d'autres appels, ce qui est un plus!PizzaBuilder.AddSauce().AddDough().AddTopping()
plus de références icilist.add(someItem)
, j'ai lu cela comme "ce code s'ajoutesomeItem
à l'list
objet". donc quand je lisPizzaBuilder.AddSauce()
, je lis ceci naturellement comme "ce code ajoute de la sauce à l'PizzaBuilder
objet". En d'autres termes, je vois l'orchestrateur (celui qui fait l'ajout) comme la méthode dans laquelle le codelist.add(someItem)
ouPizzaBuilder.addSauce()
est incorporé. Mais je pense juste que l'exemple de PizzaBuilder est un peu artificiel. Votre exemple deMyObject.SpecifySomeParameter(asdasd)
fonctionne bien pour moi.Juste mes 2 cents;
Le chaînage de méthodes rend le débogage délicat: - Vous ne pouvez pas mettre le point d'arrêt dans un point concis afin de pouvoir mettre le programme en pause exactement où vous le souhaitez - Si l'une de ces méthodes lève une exception et que vous obtenez un numéro de ligne, vous n'avez aucune idée quelle méthode dans la «chaîne» a causé le problème.
Je pense que c'est généralement une bonne pratique de toujours écrire des lignes très courtes et concises. Chaque ligne ne doit faire qu'un seul appel de méthode. Préférez plus de lignes à des lignes plus longues.
EDIT: le commentaire mentionne que le chaînage de méthode et le saut de ligne sont séparés. C'est vrai. Cependant, selon le débogueur, il peut être possible ou non de placer un point d'arrêt au milieu d'une instruction. Même si vous le pouvez, l'utilisation de lignes séparées avec des variables intermédiaires vous donne beaucoup plus de flexibilité et tout un tas de valeurs que vous pouvez examiner dans la fenêtre de surveillance qui facilite le processus de débogage.
la source
Personnellement, je préfère les méthodes de chaînage qui n'agissent que sur l'objet d'origine, par exemple en définissant plusieurs propriétés ou en appelant des méthodes de type utilitaire.
Je ne l'utilise pas lorsqu'une ou plusieurs des méthodes chaînées renverraient un objet autre que foo dans mon exemple. Bien que syntaxiquement, vous puissiez enchaîner n'importe quoi tant que vous utilisez l'API correcte pour cet objet dans la chaîne, le changement d'objets IMHO rend les choses moins lisibles et peut être vraiment déroutant si les API des différents objets présentent des similitudes. Si vous faites des appels de méthode vraiment commun à la fin (
.toString()
,.print()
, peu importe) quel objet vous agissez en fin de compte sur? Quelqu'un lisant le code avec désinvolture pourrait ne pas comprendre qu'il s'agirait d'un objet retourné implicitement dans la chaîne plutôt que de la référence d'origine.Le chaînage de différents objets peut également entraîner des erreurs nulles inattendues. Dans mes exemples, en supposant que foo est valide, tous les appels de méthode sont "sûrs" (par exemple, valides pour foo). Dans l'exemple du PO:
... il n'y a aucune garantie (en tant que développeur externe regardant le code) que getSchedule retournera réellement un objet de planification valide et non nul. En outre, le débogage de ce style de code est souvent beaucoup plus difficile car de nombreux IDE n'évalueront pas l'appel de méthode au moment du débogage comme un objet que vous pouvez inspecter. OMI, chaque fois que vous pourriez avoir besoin d'un objet à inspecter à des fins de débogage, je préfère l'avoir dans une variable explicite.
la source
Participant
n'ait pas de méthode valide,Schedule
alors lagetSchedule
méthode est conçue pour renvoyer unMaybe(of Schedule)
type et lasaveTo
méthode est conçue pour accepter unMaybe
type.Martin Fowler a une bonne discussion ici:
la source
À mon avis, le chaînage de méthodes est un peu une nouveauté. Bien sûr, ça a l'air cool mais je n'y vois aucun avantage réel.
Comment est:
pas mieux que:
L'exception peut être lorsque addObject () renvoie un nouvel objet, auquel cas le code déchaîné peut être un peu plus encombrant comme:
la source
C'est dangereux car vous pouvez dépendre de plus d'objets que prévu, comme alors votre appel renvoie une instance d'une autre classe:
Je vais donner un exemple:
foodStore est un objet composé de nombreux magasins d'alimentation que vous possédez. foodstore.getLocalStore () retourne un objet qui contient des informations sur le magasin le plus proche du paramètre. getPriceforProduct (n'importe quoi) est une méthode de cet objet.
Ainsi, lorsque vous appelez foodStore.getLocalStore (paramètres) .getPriceforProduct (n'importe quoi)
vous dépendez non seulement de FoodStore comme vous l'avez pensé, mais aussi de LocalStore.
Si getPriceforProduct (quelque chose) change un jour, vous devez modifier non seulement FoodStore mais aussi la classe qui a appelé la méthode chaînée.
Vous devez toujours viser un couplage lâche entre les classes.
Cela étant dit, j'aime personnellement les enchaîner lors de la programmation de Ruby.
la source
Beaucoup utilisent l'enchaînement de méthodes comme une forme de commodité plutôt que d'avoir à l'esprit des problèmes de lisibilité. Le chaînage de méthodes est acceptable s'il implique d'effectuer la même action sur le même objet - mais seulement s'il améliore réellement la lisibilité, et pas seulement pour écrire moins de code.
Malheureusement, beaucoup utilisent le chaînage de méthodes selon les exemples donnés dans la question. Bien qu'ils puissent toujours être rendus lisibles, ils provoquent malheureusement un couplage élevé entre plusieurs classes, ce n'est donc pas souhaitable.
la source
Cela semble assez subjectif.
L'enchaînement de méthodes n'est pas quelque chose qui est intrinsèquement mauvais ou bon imo.
La lisibilité est la chose la plus importante.
(Considérez également que le fait d'avoir un grand nombre de méthodes enchaînées rendra les choses très fragiles si quelque chose change)
la source
Avantages du chaînage,
c'est-à-dire là où j'aime l'utiliser
Un avantage du chaînage que je n'ai pas vu mentionné était la possibilité de l'utiliser lors de l'initiation des variables, ou lors du passage d'un nouvel objet à une méthode, je ne sais pas si c'est une mauvaise pratique ou non.
Je sais que c'est un exemple artificiel mais disons que vous avez les cours suivants
Et disons que vous n'avez pas accès à la classe de base, ou dites que les valeurs par défaut sont dynamiques, basées sur le temps, etc. Oui, vous pouvez instancier alors, puis changer les valeurs mais cela peut devenir fastidieux, surtout si vous ne faites que passer les valeurs d'une méthode:
Mais n'est-ce pas simplement beaucoup plus facile à lire:
Ou, qu'en est-il d'un membre de la classe?
contre
C'est ainsi que j'utilise le chaînage, et généralement mes méthodes sont juste pour la configuration, elles ne font donc que 2 lignes, définissez une valeur, puis
Return Me
. Pour nous, il a nettoyé d'énormes lignes très difficiles à lire et à comprendre le code en une seule ligne qui se lit comme une phrase. quelque chose commeVs Quelque chose comme
Au détriment du chaînage,
c'est-à-dire là où je n'aime pas l'utiliser
Je n'utilise pas le chaînage quand il y a beaucoup de paramètres à passer aux routines, principalement parce que les lignes deviennent très longues, et comme l'OP l'a mentionné, cela peut devenir déroutant lorsque vous appelez des routines à d'autres classes pour passer à l'une des les méthodes de chaînage.
Il y a aussi le souci qu'une routine renvoie des données non valides, donc jusqu'à présent, je n'ai utilisé le chaînage que lorsque je retourne la même instance appelée. Comme cela a été souligné, si vous enchaînez entre les classes, vous rendez le débogage plus difficile (lequel a renvoyé null?) Et pouvez augmenter le couplage de dépendances entre les classes.
Conclusion
Comme tout dans la vie et la programmation, le chaînage n'est ni bon, ni mauvais si vous pouvez éviter le mauvais alors le chaînage peut être un grand avantage.
J'essaye de suivre ces règles.
la source
Le chaînage de méthodes peut permettre de concevoir directement des DSL avancés en Java. En substance, vous pouvez modéliser au moins ces types de règles DSL:
Ces règles peuvent être implémentées à l'aide de ces interfaces
Avec ces règles simples, vous pouvez implémenter des DSL complexes tels que SQL directement en Java, comme le fait jOOQ , une bibliothèque que j'ai créée. Voir un exemple SQL assez complexe tiré de mon blog ici:
Un autre bel exemple est jRTF , un petit DSL conçu pour la certification de documents RTF directement en Java. Un exemple:
la source
Le chaînage de méthodes peut être simplement une nouveauté dans la plupart des cas, mais je pense qu'il a sa place. Un exemple peut être trouvé dans l'utilisation de l'enregistrement actif de CodeIgniter :
Cela semble beaucoup plus propre (et a plus de sens, à mon avis) que:
C'est vraiment subjectif; Tout le monde a sa propre opinion.
la source
Je pense que l'erreur principale est de penser qu'il s'agit d'une approche orientée objet en général alors qu'en fait, il s'agit plus d'une approche de programmation fonctionnelle qu'autre chose.
Les principales raisons pour lesquelles je l'utilise sont à la fois pour la lisibilité et pour éviter que mon code ne soit inondé de variables.
Je ne comprends pas vraiment de quoi parlent les autres quand ils disent que cela nuit à la lisibilité. C'est l'une des formes de programmation les plus concises et cohérentes que j'ai utilisées.
Aussi ceci:
convertTextToVoice.LoadText ("source.txt"). ConvertToVoice ("destination.wav");
est la façon dont je l'utilise généralement. L'utiliser pour enchaîner x nombre de paramètres n'est pas la façon dont je l'utilise généralement. Si je voulais mettre un nombre x de paramètres dans un appel de méthode, j'utiliserais la syntaxe params :
public void foo (objet params [] éléments)
Et lancez les objets en fonction du type ou utilisez simplement un tableau ou une collection de types de données en fonction de votre cas d'utilisation.
la source
Je suis d'accord, j'ai donc changé la façon dont une interface fluide était implémentée dans ma bibliothèque.
Avant:
Après:
Dans l'implémentation "avant", les fonctions ont modifié l'objet et se sont terminées par
return this
. J'ai changé l'implémentation pour renvoyer un nouvel objet du même type .Mon raisonnement pour ce changement :
La valeur de retour n'avait rien à voir avec la fonction, elle était purement là pour supporter la partie chaînage, cela aurait dû être une fonction vide selon la POO.
Le chaînage de méthodes dans les bibliothèques système l'implémente également de cette façon (comme linq ou string):
L'objet d'origine reste intact, ce qui permet à l'utilisateur de l'API de décider quoi en faire. Il permet:
Une implémentation de copie peut également être utilisée pour créer des objets:
Où la
setBackground(color)
fonction change l'instance et ne renvoie rien (comme c'est supposé le faire) .Le comportement des fonctions est plus prévisible (voir points 1 et 2).
L'utilisation d'un nom de variable court peut également réduire l'encombrement du code, sans forcer une API sur le modèle.
Conclusion:
À mon avis, une interface fluide qui utilise une
return this
implémentation est tout simplement fausse.la source
return this
gérer ou non. C'est mon avis qu'il obtient une api fluide d'une manière "non naturelle". (Raison supplémentaire 6: pour montrer une alternative non fluide, qui n'a pas la surcharge / fonctionnalité ajoutée)Le point totalement manqué ici, c'est que l'enchaînement de méthodes permet le DRY . C'est un substitut efficace pour le "avec" (qui est mal implémenté dans certaines langues).
Cela compte pour la même raison que DRY compte toujours; si A s'avère être une erreur et que ces opérations doivent être effectuées sur B, vous n'avez besoin de mettre à jour qu'à 1 endroit, pas 3.
Pragmatiquement, l'avantage est faible dans ce cas. Pourtant, un peu moins de frappe, un peu plus robuste (DRY), je vais le prendre.
la source
Je déteste généralement le chaînage de méthodes car je pense que cela aggrave la lisibilité. La compacité est souvent confondue avec la lisibilité, mais ce ne sont pas les mêmes termes. Si vous faites tout dans une seule instruction, alors c'est compact, mais c'est moins lisible (plus difficile à suivre) la plupart du temps que de le faire dans plusieurs instructions. Comme vous l'avez remarqué, à moins que vous ne puissiez garantir que la valeur de retour des méthodes utilisées est la même, le chaînage des méthodes sera une source de confusion.
1.)
contre
2.)
contre
3.)
contre
Comme vous pouvez le voir, vous ne gagnez presque rien, car vous devez ajouter des sauts de ligne à votre instruction unique pour la rendre plus lisible et vous devez ajouter une indentation pour indiquer clairement que vous parlez d'objets différents. Eh bien, si je voulais utiliser un langage basé sur l'identification, j'apprendrais Python au lieu de le faire, sans oublier que la plupart des IDE supprimeront l'indentation en formatant automatiquement le code.
Je pense que le seul endroit où ce type de chaînage peut être utile est de canaliser des flux dans CLI ou de joindre plusieurs requêtes ensemble dans SQL. Les deux ont un prix pour plusieurs déclarations. Mais si vous voulez résoudre des problèmes complexes, vous vous retrouverez même avec ceux qui en paient le prix et qui écrivent le code dans plusieurs instructions en utilisant des variables ou en écrivant des scripts bash et des procédures ou vues stockées.
Comme des interprétations DRY: "Évitez la répétition de la connaissance (pas la répétition de texte)." et "Tapez moins, ne répétez même pas les textes.", le premier ce que signifie vraiment le principe, mais le second est un malentendu commun car beaucoup de gens ne peuvent pas comprendre des conneries trop compliquées comme "Chaque connaissance doit avoir une seule, sans ambiguïté, représentation faisant autorité dans un système ". Le second est la compacité à tout prix, qui rompt dans ce scénario, car elle aggrave la lisibilité. La première interprétation rompt par DDD lorsque vous copiez du code entre des contextes limités, car le couplage lâche est plus important dans ce scénario.
la source
Le bon:
Le mauvais:
Général:
Une bonne approche consiste à ne pas utiliser le chaînage en général jusqu'à ce que des situations surviennent ou que des modules spécifiques y soient particulièrement adaptés.
Le chaînage peut nuire gravement à la lisibilité dans certains cas, en particulier lors de la pesée aux points 1 et 2.
Lors de l'accasation, il peut être mal utilisé, comme au lieu d'une autre approche (passer un tableau par exemple) ou mélanger des méthodes de manière bizarre (parent.setSomething (). GetChild (). SetSomething (). GetParent (). SetSomething ()).
la source
Réponse avisée
Le plus gros inconvénient du chaînage est qu'il peut être difficile pour le lecteur de comprendre comment chaque méthode affecte l'objet d'origine, si c'est le cas, et quel type chaque méthode retourne.
Quelques questions:
Le débogage, dans la plupart des langages, peut en effet être plus difficile avec le chaînage. Même si chaque étape de la chaîne est sur sa propre ligne (ce qui va à l'encontre de l'objectif du chaînage), il peut être difficile d'inspecter la valeur renvoyée après chaque étape, en particulier pour les méthodes non mutantes.
Les temps de compilation peuvent être plus lents en fonction du langage et du compilateur, car les expressions peuvent être beaucoup plus complexes à résoudre.
Je crois que comme pour tout, le chaînage est une bonne solution qui peut être pratique dans certains scénarios. Il doit être utilisé avec prudence, en comprenant les implications et en limitant le nombre d'éléments de la chaîne à quelques-uns.
la source
Dans les langages typés (qui manquent
auto
ou équivalent), cela évite à l'implémenteur d'avoir à déclarer les types des résultats intermédiaires.Pour des chaînes plus longues, vous pourriez avoir affaire à plusieurs types intermédiaires différents, vous devrez déclarer chacun d'eux.
Je crois que cette approche s'est vraiment développée en Java où a) tous les appels de fonction sont des invocations de fonctions membres, et b) des types explicites sont requis. Bien sûr, il y a un compromis ici en termes, perdant une certaine explication, mais dans certaines situations, certaines personnes trouvent que cela vaut la peine.
la source