web-dev-qa-db-fra.com

Méthode de chaînage - pourquoi est-ce une bonne pratique ou pas?

Method chaining est la pratique des méthodes d'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 il produit un code lisible, ou une "interface fluide". Cependant, pour moi, il semble plutôt casser la notation d'appel d'objet impliquée par l'orientation de l'objet lui-même - le code résultant ne représente pas l'exécution d'actions sur le résultat d'une méthode précédente, ce qui correspond généralement au code orienté objet. devrait fonctionner:

participant.getSchedule('monday').saveTo('monnday.file')

Cette différence parvient à créer deux significations différentes pour la notation par points de "l'appel de l'objet résultant": dans le contexte du chaînage, l'exemple ci-dessus se lirait comme enregistrant l'objet participant, même si l'exemple est en cours fait destiné à enregistrer l'objet de calendrier reçu par getSchedule.

Je comprends que la différence ici est de savoir si la méthode appelée doit renvoyer 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, mais 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 agit sur quelque chose lié au résultat de l'appel précédent - avec chaînage, cette hypothèse se rompt et je dois traiter sémantiquement toute la chaîne pour comprendre ce que l’objet réel appelé est réellement. 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 font référence 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 réellement des chaînes dans lesquelles le contexte change (est-ce que c’est?), Mais même dans ce cas, vous devrez constamment vérifier si les chaînes de points qui se ressemblent sont réellement conservées dans le même contexte ou si elles ne fonctionnent que sur le résultat .

Il me semble que, si le chaînage superficiel produit un code lisible, surcharger la signification de la notation par points ne fait que créer davantage de confusion. Comme je ne me considère pas comme un gourou de la programmation, je suppose que la faute en est à moi. Alors: Qu'est-ce qui me manque? Est-ce que je comprends mal la méthode de chaînage? Existe-t-il des cas où l'enchaînement des méthodes est particulièrement efficace ou des cas particulièrement difficiles?

Note: Je comprends que cette question pourrait être interprétée comme une déclaration d'opinion masquée sous forme de question. Cependant, je ne veux pas vraiment comprendre pourquoi le chaînage est considéré comme une bonne pratique, et où puis-je me tromper en pensant qu'il brise la notation inhérente orientée objet.

133
Ilari Kajaste

Je conviens que c'est subjectif. Pour la plupart, j’évite l’enchaînement de méthodes, mais récemment, j’ai également trouvé un cas où c’était la bonne chose - j’avais une méthode qui acceptait environ 10 paramètres et qui nécessitait davantage, mais pour la plupart du temps, il suffisait de spécifier un paramètre. peu. Avec les dérogations, cela est devenu très lourd très rapidement. Au lieu de cela, j'ai opté pour l'approche de chaînage:

MyObject.Start()
    .SpecifySomeParameter(asdasd)
    .SpecifySomeOtherParameter(asdasd)
    .Execute();

Cela ressemble à un motif d'usine. L'approche de chaînage de méthodes était optionnelle, 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 courante 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.

71
Vilx-

Juste mes 2 cents;

Le chaînage des méthodes rend le débogage délicat: - Vous ne pouvez pas placer le point d'arrêt dans un point concis afin de pouvoir suspendre le programme exactement là 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 ne savez pas quelle méthode de 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 doit juste faire un appel de méthode. Préfère plus de lignes aux lignes plus longues.

EDIT: le commentaire mentionne que les méthodes de chaînage et de saut de ligne sont séparées. C'est vrai. Toutefois, 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 un tas de valeurs que vous pouvez examiner dans la fenêtre de surveillance qui facilite le processus de débogage.

72
RAY

Personnellement, je préfère les méthodes de chaînage qui agissent uniquement sur l'objet d'origine, par exemple. définir plusieurs propriétés ou appeler des méthodes de type utilitaire.

foo.setHeight(100).setWidth(50).setColor('#ffffff');
foo.moveTo(100,100).highlight();

Je ne l'utilise pas lorsqu'une ou plusieurs des méthodes chaînées renvoient un objet autre que foo dans mon exemple. Bien que vous puissiez syntaxiquement chaîner n'importe quoi tant que vous utilisez la bonne API pour cet objet de la chaîne, modifier les objets à mon humble avis rend les choses moins lisibles et peut être très déroutant si les API des différents objets présentent des similitudes. Si vous faites un appel de méthode vraiment commun à la fin (.toString(), .print(), peu importe), sur quel objet agissez-vous en dernier ressort? Quelqu'un qui lit le code de manière nonchage risque de ne pas comprendre qu'il s'agirait d'un objet renvoyé 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 null inattendues. Dans mes exemples, en supposant que foo est valide, tous les appels de méthode sont "sans danger" (par exemple, valables pour foo). Dans l'exemple du PO:

participant.getSchedule('monday').saveTo('monnday.file')

... il n'y a aucune garantie (en tant que développeur externe examinant le code) que getSchedule renvoie en réalité un objet de planification valide et non null. En outre, le débogage de ce style de code est souvent beaucoup plus difficile, car de nombreux IDE n'évaluent pas l'appel de méthode au moment du débogage en tant qu'objet que vous pouvez inspecter. IMO, 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.

37
Brian Moeskau

Martin Fowler a une bonne discussion ici:

Méthode d'enchaînement

Quand l'utiliser

La méthode de chaînage peut ajouter beaucoup à la lisibilité d'un DSL interne et en conséquence est devenu presque un synonum pour les DSL internes dans certains esprits. La méthode est la meilleure chaîne, cependant, quand il est utilisé conjointement avec d'autres combinaisons de fonctions.

La méthode de chaînage est particulièrement efficace avec les grammaires comme parent :: = (ça | ça) *. L'utilisation de différents Méthodes fournit un moyen lisible de voir quel argument vient ensuite . De même, les arguments optionnels peuvent être facilement sauté avec Method Chaînage Une liste de clauses obligatoires, comme parent :: = la première seconde ne le fait pas fonctionne si bien avec la forme de base, bien qu'il puisse être bien soutenu par en utilisant des interfaces progressives. La plupart de au moment où je préférerais Fonction imbriquée pour ce cas.

Le plus gros problème pour Method Le chaînage est le problème de finition . Bien qu’il existe des solutions de contournement, généralement si vous rencontrez cela, vous êtes mieux lotis en utilisant une fonction imbriquée. Imbriqué La fonction est également un meilleur choix si vous vous retrouvez dans le pétrin avec Variables de contexte.

22
Dirk Vollmar

À mon avis, le chaînage des méthodes est une nouveauté. Bien sûr, ça a l'air cool mais je n'y vois aucun avantage réel.

Comment est:

someList.addObject("str1").addObject("str2").addObject("str3")

mieux que:

someList.addObject("str1")
someList.addObject("str2")
someList.addObject("str3")

L'exception peut se produire lorsque addObject () renvoie un nouvel objet. Dans ce cas, le code non chaîné peut être un peu plus lourd, par exemple:

someList = someList.addObject("str1")
someList = someList.addObject("str2")
someList = someList.addObject("str3")
20
Tom Dalling

C'est dangereux parce que vous pouvez dépendre d'un plus grand nombre d'objets que prévu, comme si votre appel renvoyait une instance d'une autre classe:

Je vais donner un exemple:

foodStore est un objet composé de nombreux magasins alimentaires que vous possédez foodstore.getLocalStore () renvoie un objet contenant des informations sur le magasin le plus proche du paramètre. getPriceforProduct (n'importe quoi) est une méthode de cet objet.

Alors, quand vous appelez foodStore.getLocalStore (paramètres) .getPriceforProduct (n'importe quoi)

vous dépendez non seulement du FoodStore, mais aussi du LocalStore.

Si getPriceforProduct (quoi que ce soit) change jamais, vous devez changer non seulement FoodStore mais également 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 chaîner pour programmer Ruby.

8
rprandi

Le chaînage de méthodes peut permettre de concevoir directement des DSL avancés en Java. En substance, vous pouvez au moins modéliser ces types de règles DSL:

1. SINGLE-Word
2. PARAMETERISED-Word parameter
3. Word1 [ OPTIONAL-Word]
4. Word2 { Word-CHOICE-A | Word-CHOICE-B }
5. Word3 [ , Word3 ... ]

Ces règles peuvent être implémentées en utilisant ces interfaces

// Initial interface, entry point of the DSL
interface Start {
  End singleWord();
  End parameterisedWord(String parameter);
  Intermediate1 Word1();
  Intermediate2 Word2();
  Intermediate3 Word3();
}

// Terminating interface, might also contain methods like execute();
interface End {}

// Intermediate DSL "step" extending the interface that is returned
// by optionalWord(), to make that method "optional"
interface Intermediate1 extends End {
  End optionalWord();
}

// Intermediate DSL "step" providing several choices (similar to Start)
interface Intermediate2 {
  End wordChoiceA();
  End wordChoiceB();
}

// Intermediate interface returning itself on Word3(), in order to allow for
// repetitions. Repetitions can be ended any time because this interface
// extends End
interface Intermediate3 extends End {
  Intermediate3 Word3();
}

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:

create().select(
    r1.ROUTINE_NAME,
    r1.SPECIFIC_NAME,
    decode()
        .when(exists(create()
            .selectOne()
            .from(PARAMETERS)
            .where(PARAMETERS.SPECIFIC_SCHEMA.equal(r1.SPECIFIC_SCHEMA))
            .and(PARAMETERS.SPECIFIC_NAME.equal(r1.SPECIFIC_NAME))
            .and(upper(PARAMETERS.PARAMETER_MODE).notEqual("IN"))),
                val("void"))
        .otherwise(r1.DATA_TYPE).as("data_type"),
    r1.NUMERIC_PRECISION,
    r1.NUMERIC_SCALE,
    r1.TYPE_UDT_NAME,
    decode().when(
    exists(
        create().selectOne()
            .from(r2)
            .where(r2.ROUTINE_SCHEMA.equal(getSchemaName()))
            .and(r2.ROUTINE_NAME.equal(r1.ROUTINE_NAME))
            .and(r2.SPECIFIC_NAME.notEqual(r1.SPECIFIC_NAME))),
        create().select(count())
            .from(r2)
            .where(r2.ROUTINE_SCHEMA.equal(getSchemaName()))
            .and(r2.ROUTINE_NAME.equal(r1.ROUTINE_NAME))
            .and(r2.SPECIFIC_NAME.lessOrEqual(r1.SPECIFIC_NAME)).asField())
    .as("overload"))
.from(r1)
.where(r1.ROUTINE_SCHEMA.equal(getSchemaName()))
.orderBy(r1.ROUTINE_NAME.asc())
.fetch()

Un autre exemple intéressant est jRTF , un petit DSL conçu pour certifier RTF des documents directement en Java. Un exemple:

rtf()
  .header(
    color( 0xff, 0, 0 ).at( 0 ),
    color( 0, 0xff, 0 ).at( 1 ),
    color( 0, 0, 0xff ).at( 2 ),
    font( "Calibri" ).at( 0 ) )
  .section(
        p( font( 1, "Second paragraph" ) ),
        p( color( 1, "green" ) )
  )
).out( out );
6
Lukas Eder

Avantages du chaînage
C'est-à-dire où j'aime bien l'utiliser

L'un des avantages du chaînage que je n'ai pas vu mentionné est la possibilité de l'utiliser lors du lancement d'une variable ou lors du passage d'un nouvel objet à une méthode, sans savoir si cela est une mauvaise pratique ou non.

Je sais que c'est un exemple artificiel mais disons que vous avez les classes suivantes

Public Class Location
   Private _x As Integer = 15
   Private _y As Integer = 421513

   Public Function X() As Integer
      Return _x
   End Function
   Public Function X(ByVal value As Integer) As Location
      _x = value
      Return Me
   End Function

   Public Function Y() As Integer
      Return _y
   End Function
   Public Function Y(ByVal value As Integer) As Location
      _y = value
      Return Me
   End Function

   Public Overrides Function toString() As String
      Return String.Format("{0},{1}", _x, _y)
   End Function
End Class

Public Class HomeLocation
   Inherits Location

   Public Overrides Function toString() As String
      Return String.Format("Home Is at: {0},{1}", X(), Y())
   End Function
End Class

Et dites 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 puis modifier les valeurs, mais cela peut devenir fastidieux, surtout si vous ne faites que passer. les valeurs d'une méthode:

  Dim loc As New HomeLocation()
  loc.X(1337)
  PrintLocation(loc)

Mais n’est-ce pas beaucoup plus facile à lire:

  PrintLocation(New HomeLocation().X(1337))

Ou, qu'en est-il d'un membre de la classe?

Public Class Dummy
   Private _locA As New Location()
   Public Sub New()
      _locA.X(1337)
   End Sub
End Class

contre

Public Class Dummy
   Private _locC As Location = New Location().X(1337)
End Class

C’est ainsi que j’utilise le chaînage et, en général, mes méthodes ne servent qu’à 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.

New Dealer.CarPicker().Subaru.WRX.SixSpeed.TurboCharged.BlueExterior.GrayInterior.Leather.HeatedSeats

Vs quelque chose comme

New Dealer.CarPicker(Dealer.CarPicker.Makes.Subaru
                   , Dealer.CarPicker.Models.WRX
                   , Dealer.CarPicker.Transmissions.SixSpeed
                   , Dealer.CarPicker.Engine.Options.TurboCharged
                   , Dealer.CarPicker.Exterior.Color.Blue
                   , Dealer.CarPicker.Interior.Color.Gray
                   , Dealer.CarPicker.Interior.Options.Leather
                   , Dealer.CarPicker.Interior.Seats.Heated)

préjudice du chaînage
C'est-à-dire où je n'aime pas l'utiliser

Je n'utilise pas l'enchaînement 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 être déroutant lorsque vous appelez des routines à d'autres classes pour les passer à l'une des les méthodes de chaînage.

Il existe également le souci qu’une routine renvoie des données non valides, c’est pourquoi jusqu’à présent, j’ai utilisé le chaînage uniquement lorsque je renvoie la même instance appelée. Comme on l'a fait remarquer, si vous enchaînez des classes, vous rendez plus difficile le débogage (lequel est retourné null?) Et pouvez augmenter le couplage des dépendances entre les classes.

Conclusion

Comme pour tout ce qui se passe dans la vie et la programmation, l'enchaînement n'est ni bon ni mauvais si vous pouvez éviter le mauvais, alors l'enchaînement peut être un grand avantage.

J'essaie de suivre ces règles.

  1. Essayez de ne pas enchaîner les cours
  2. Faire des routines spécifiquement pour Chaining
  3. Ne faites qu'une seule chose dans une chaîne Routine
  4. Utilisez-le quand il améliore la lisibilité
  5. Utilisez-le quand cela simplifie le code
6
Apeiron

Beaucoup utilisent la méthode des chaînes comme une commodité plutôt que de se préoccuper de la lisibilité. Le chaînage de méthodes est acceptable s'il implique l'exécution de la même action sur le même objet, mais uniquement s'il améliore réellement la lisibilité, et pas seulement pour écrire moins de code.

Malheureusement, beaucoup utilisent la méthode de chaînage selon les exemples donnés dans la question. Bien qu'ils puissent toujours être rendus lisibles, ils causent malheureusement un couplage élevé entre plusieurs classes, ce qui n'est donc pas souhaitable.

6
aberrant80

Cela semble un peu subjectif.

Le chaînage des méthodes n’est pas ce qui est intrinsèquement mauvais ou bon.

La lisibilité est la chose la plus importante.

(Considérez également qu'avoir un grand nombre de méthodes enchaînées rendra les choses très fragiles si quelque chose change.)

6
John Nicholas

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 Utilisation de l'enregistrement actif de CodeIgniter :

$this->db->select('something')->from('table')->where('id', $id);

Cela semble beaucoup plus propre (et plus logique, à mon avis) que:

$this->db->select('something');
$this->db->from('table');
$this->db->where('id', $id);

C'est vraiment subjectif; Tout le monde a sa propre opinion.

3
Nathan

Je suis d'accord, j'ai donc changé la façon dont une interface fluide a été implémentée dans ma bibliothèque.

Avant:

collection.orderBy("column").limit(10);

Après:

collection = collection.orderBy("column").limit(10);

Dans l'implémentation "before", les fonctions ont modifié l'objet et se sont terminées par return this. J'ai modifié l'implémentation pour renvoie un nouvel objet du même type .

Mon raisonnement pour ce changement :

  1. La valeur de retour n'a rien à voir avec la fonction, elle est uniquement là pour prendre en charge la partie d'enchaînement. Elle aurait dû être une fonction vide conformément à la POO. 

  2. Les méthodes de chaînage dans les bibliothèques système implémentent également cette méthode (comme linq ou string):

    myText = myText.trim().toUpperCase();
    
  3. L'objet d'origine reste intact, ce qui permet à l'utilisateur de l'API de décider quoi faire avec. Cela permet:

    page1 = collection.limit(10);
    page2 = collection.offset(10).limit(10);
    
  4. Une implémentation copy peut également être utilisée pour construire des objets:

    painting = canvas.withBackground('white').withPenSize(10);
    

    Où la fonction setBackground(color) change l'instance et ne renvoie rien (comme c'est supposé le faire)

  5. Le comportement des fonctions est plus prévisible (voir points 1 et 2).

  6. L'utilisation d'un nom de variable court peut également réduire l'encombrement du code, sans imposer une API au modèle.

    var p = participant; // create a reference
    p.addSchedule(events[1]);p.addSchedule(events[2]);p.setStatus('attending');p.save()
    

Conclusion:
À mon avis, une interface fluide qui utilise une implémentation de return this est tout simplement fausse.

2
Bob Fanger

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 davantage d'une approche de programmation fonctionnelle qu'autre chose.

Je l’utilise principalement pour des raisons de 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");

voici comment je l’utilise typiquement. L’utiliser pour chaîner x nombre de paramètres n’est pas la façon dont je l’utilise généralement. Si je voulais mettre x nombre de paramètres dans un appel de méthode, j'utiliserais la syntaxe params :

public void foo (objet params [] items)

Et convertissez les objets en fonction du type ou utilisez simplement un tableau ou une collection de types de données selon votre cas d'utilisation.

2
Shane Thorndike

Le point totalement omis ici est que cette méthode permet de SEC. C'est un remplaçant efficace pour le "avec" (qui est mal implémenté dans certaines langues). 

A.method1().method2().method3(); // one A

A.method1();
A.method2();
A.method3(); // repeating A 3 times

C'est important pour la même raison DRY est toujours important; Si A s'avère être une erreur et que ces opérations doivent être effectuées sur B, il vous suffit de mettre à jour à un endroit, pas à trois. 

De manière pragmatique, l'avantage est faible dans ce cas. Toujours, un peu moins de frappe, un peu plus robuste (DRY), je vais le prendre. 

0
Anfurny

Je déteste généralement l’enchaînement de méthodes parce que je pense que cela empire 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 déclaration, c'est compact, mais la plupart du temps, il est moins lisible (plus difficile à suivre) que de le faire dans plusieurs déclarations. Comme vous l'avez constaté, à moins que vous ne puissiez garantir que les valeurs des méthodes utilisées soient identiques, le chaînage des méthodes sera source de confusion. 

1.)

participant
    .addSchedule(events[1])
    .addSchedule(events[2])
    .setStatus('attending')
    .save();

contre

participant.addSchedule(events[1]);
participant.addSchedule(events[2]);
participant.setStatus('attending');
participant.save()

2.)

participant
    .getSchedule('monday')
        .saveTo('monnday.file');

contre

mondaySchedule = participant.getSchedule('monday');
mondaySchedule.saveTo('monday.file');

3.)

participant
    .attend(event)
    .setNotifications('silent')
    .getSocialStream('Twitter')
        .postStatus('Joining '+event.name)
        .follow(event.getSocialId('Twitter'));

contre

participant.attend(event);
participant.setNotifications('silent')
Twitter = participant.getSocialStream('Twitter')
Twitter.postStatus('Joining '+event.name)
Twitter.follow(event.getSocialId('Twitter'));

Comme vous pouvez le constater, vous ne gagnez presque rien, car vous devez ajouter des sauts de ligne à votre déclaration 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 plutôt Python au lieu de le faire, sans compter que la plupart des IDE supprimeraient 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 la CLI ou de joindre des requêtes multiples en SQL. Les deux ont un prix pour plusieurs déclarations. Mais si vous voulez résoudre des problèmes complexes, vous finirez par payer ceux qui paient le prix et écrire le code dans plusieurs instructions en utilisant des variables ou en écrivant des scripts bash et des procédures ou vues stockées.

A partir des interprétations DRY: "Évitez la répétition de connaissances (pas la répétition de texte)." et "Tapez moins, ne répétez même pas les textes.", le premier ce que le principe signifie réellement, mais le second est un malentendu courant car beaucoup de gens ne peuvent pas comprendre des conneries trop compliquées comme "Chaque savoir doit avoir un seul, sans ambiguïté, représentation faisant autorité dans un système ". Le second est la compacité à tout prix, ce qui rompt dans ce scénario, car il empire la lisibilité. La première interprétation est interrompue par DDD lorsque vous copiez du code entre des contextes liés, car un couplage lâche est plus important dans ce scénario.

0
inf3rno

Réponse d'opinion

L'inconvénient majeur du chaînage est qu'il peut être difficile pour le lecteur de comprendre comment chaque méthode affecte l'objet d'origine, le cas échéant, et quel type est renvoyée par chaque méthode.

Quelques questions:

  • Les méthodes de la chaîne renvoient-elles un nouvel objet ou le même objet muté?
  • Toutes les méthodes de la chaîne renvoient-elles le même type?
  • Si non, comment est indiqué quand un type de la chaîne change?
  • La valeur renvoyée par la dernière méthode peut-elle être ignorée en toute sécurité?

Le débogage, dans la plupart des langues, 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 du but de l'enchaînement), 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 pense que comme pour tout, l'enchaînement est une bonne solution qui peut être pratique dans certains cas. Il convient de l'utiliser avec prudence, de comprendre les implications et de limiter le nombre d'éléments de la chaîne à quelques-uns.

0
Eneko Alonso

Le bon:

  1. C'est concis, mais vous permet de mettre plus d'une seule ligne avec élégance.
  2. Vous pouvez parfois éviter l'utilisation d'une variable, ce qui peut parfois être utile.
  3. Cela peut fonctionner mieux.

Le mauvais:

  1. Vous implémentez des retours, en ajoutant essentiellement des fonctionnalités aux méthodes sur les objets qui ne font pas vraiment partie de ce que ces méthodes sont censées faire. Il renvoie quelque chose que vous avez déjà uniquement pour économiser quelques octets.
  2. Il cache les changements de contexte lorsqu'une chaîne en mène à une autre. Vous pouvez obtenir cela avec des accesseurs, sauf que c'est assez clair lorsque le contexte change.
  3. L'enchaînement sur plusieurs lignes a l'air moche, ne joue pas bien avec l'indentation et peut semer la confusion chez un opérateur (surtout dans les langues avec ASI).
  4. Si vous souhaitez commencer à renvoyer quelque chose d'autre qui soit utile pour une méthode chaînée, vous aurez probablement plus de difficulté à la réparer ou à poser plus de problèmes.
  5. Vous confiez le contrôle à une entité à laquelle vous n'auriez normalement pas recours, même dans des langages strictement typés, les erreurs ainsi causées ne peuvent pas toujours être détectées.
  6. Cela peut être pire.

Général:

Une bonne approche consiste à ne pas utiliser le chaînage en général avant que des situations ne se présentent ou que des modules spécifiques ne lui conviennent particulièrement.

Le chaînage peut nuire gravement à la lisibilité dans certains cas, en particulier lors de la pesée en points 1 et 2.

Il peut être utilisé à mauvais escient, par exemple à la place d'une autre approche (passer un tableau par exemple) ou mélanger des méthodes de façon bizarre (parent.setSomething (). GetChild (). SetSomething (). GetParent (). SetSomething ()).

0
jgmjgm