Le chaînage sur les haricots est très pratique: vous n'avez pas besoin de surcharger les constructeurs, les méga constructeurs, les usines et vous donne une lisibilité accrue. Je ne peux penser à aucun inconvénient, à moins que vous ne souhaitiez que votre objet soit immuable , auquel cas il n'aurait aucun décideur de toute façon. Donc, y a-t-il une raison pour laquelle ce n'est pas une convention POO?
public class DTO {
private String foo;
private String bar;
public String getFoo() {
return foo;
}
public String getBar() {
return bar;
}
public DTO setFoo(String foo) {
this.foo = foo;
return this;
}
public DTO setBar(String bar) {
this.bar = bar;
return this;
}
}
//...//
DTO dto = new DTO().setFoo("foo").setBar("bar");
myCustomDTO = DTOBuilder.defaultDTO().withFoo("foo").withBar("bar").Build();
Je le ferais pour ne pas contredire l’idée générale selon laquelle les setters sont vides.new Foo().setBar('bar').setBaz('baz')
très "fluide". Je veux dire, bien sûr, cela pourrait être mis en œuvre exactement de la même manière, mais je m'attendrais beaucoup à lire quelque chose qui ressemble davantage àFoo().barsThe('bar').withThe('baz').andQuuxes('the quux')
Réponses:
Ma meilleure supposition: parce que c'est une violation de CQS
Vous avez une commande (modification de l'état de l'objet) et une requête (renvoyant une copie de l'état - dans ce cas, l'objet lui-même) mélangées dans la même méthode. Ce n'est pas nécessairement un problème, mais cela enfreint certaines des directives de base.
Par exemple, en C ++, std :: stack :: pop () est une commande qui renvoie void et std :: stack :: top () est une requête qui renvoie une référence à l'élément supérieur de la pile. Classiquement, vous voudriez combiner les deux, mais vous ne pouvez pas le faire et être exceptionnellement sûr. (Pas de problème en Java, car l’opérateur d’affectation en Java ne jette pas).
Si DTO était un type de valeur, vous pourriez obtenir une fin similaire avec
En outre, l’enchaînement des valeurs de retour est une douleur colossale lorsqu’il s’agit d’un héritage. Voir "Modèle de modèle curieusement récurrent"
Enfin, le constructeur par défaut devrait vous laisser un objet dans un état valide. Si vous devez exécuter plusieurs commandes pour restaurer l'objet dans un état valide, quelque chose s'est très mal passé.
la source
abstract
T getSelf()
méthode avec renvoie le type générique. Maintenant, au lieu de revenirthis
de vos setters, vousreturn getSelf()
et toute classe dominante rendent simplement le type générique en lui-même et reviennentthis
degetSelf
. De cette façon, les setters renvoient le type réel et non le type déclarant.Enregistrer quelques frappes n’est pas convaincant. Ce serait peut-être bien, mais les conventions POO accordent plus d'importance aux concepts et aux structures qu'à la frappe au clavier.
La valeur de retour n'a pas de sens.
Plus encore que ne voulant rien dire, la valeur de retour est trompeuse, car les utilisateurs peuvent s'attendre à ce que la valeur de retour ait une signification. Ils peuvent s’attendre à ce qu’il s’agisse d’un "passeur immuable"
En réalité, votre setter mute l'objet.
Cela ne fonctionne pas bien avec l'héritage.
je peux écrire
mais non
Pour moi, les numéros 1 à 3 sont les plus importants. Un code bien écrit ne concerne pas un texte arrangé agréablement. Un code bien écrit concerne les concepts fondamentaux, les relations et la structure. Le texte n'est qu'un reflet extérieur de la véritable signification du code.
la source
public class Foo<T extends Foo> {...}
avec le paramètre retourneurFoo<T>
. Aussi ces méthodes "setter", je préfère les appeler "avec" méthodes. Si la surcharge fonctionne bien, alorsFoo<T> with(Bar b) {...}
, sinonFoo<T> withBar(Bar b)
.Je ne pense pas que ce soit une convention POO, mais plutôt liée à la conception du langage et à ses conventions.
Il semble que vous aimez utiliser Java. Java a une spécification JavaBeans qui spécifie le type de retour du setter à vide, c'est-à-dire qu'il entre en conflit avec l'enchaînement des setters. Cette spécification est largement acceptée et implémentée dans une variété d’outils.
Bien sûr, vous pourriez vous demander pourquoi la chaîne ne fait pas partie des spécifications. Je ne connais pas la réponse, peut-être que ce motif n'était tout simplement pas connu / populaire à cette époque.
la source
Object
, ce qui peut entraîner le renvoi du singleton de typeVoid
si la méthode spécifievoid
comme type de retour. Par ailleurs, apparemment, "laisser un commentaire mais pas voter" est un motif d'échec pour un audit de "premier post", même s'il s'agit d'un bon commentaire. Je blâme shog pour cela.Comme d’autres l’ont dit, on parle souvent d’ interface fluide .
Normalement, les setters sont des appels qui transmettent des variables en réponse au code logique dans une application; votre classe DTO en est un exemple. Le code conventionnel lorsque les setters ne renvoient rien est normal pour cela. D'autres réponses ont expliqué de manière.
Cependant, il existe quelques cas où une interface fluide peut être une bonne solution, ils ont en commun.
Configuration de la configuration, par exemple fluent-nhibernate
Configuration des données de test dans les tests unitaires, à l'aide de TestDataBulderClasses (Object Mothers) spécial
Cependant, créer une bonne interface fluide est très difficile , donc cela ne vaut que si vous avez beaucoup de configurations «statiques». De plus, l'interface fluide ne doit pas être mélangée avec des classes «normales»; par conséquent, le modèle de construction est souvent utilisé.
la source
Je pense qu'une grande partie de la raison pour laquelle ce n'est pas une convention pour enchaîner un setter après un autre c'est parce que dans ces cas, il est plus typique de voir un objet options ou des paramètres dans un constructeur. C # a également une syntaxe d'initialiseur.
Au lieu de:
On pourrait écrire:
(en JS)
(en C #)
(en Java)
setFoo
et nesetBar
sont alors plus nécessaires pour l’initialisation et peuvent être utilisés pour une mutation ultérieure.Bien que la chaînage soit utile dans certaines circonstances, il est important de ne pas essayer de tout mettre sur une seule ligne uniquement pour réduire les caractères de nouvelle ligne.
Par exemple
rend plus difficile la lecture et la compréhension de ce qui se passe. Reformatage à:
Est beaucoup plus facile à comprendre, et rend "l'erreur" dans la première version plus évidente. Une fois que vous avez restructuré le code dans ce format, vous n’avez plus aucun avantage réel à:
la source
/
to représente un saut de ligne]With Foo(1234) / .x = 23 / .y = 47
serait équivalent àDim temp=Foo(1234) / temp.x = 23 / temp.y = 47
. Une telle syntaxe ne crée pas d'ambiguïté car.x
elle ne peut par elle-même signifier que de se lier à l'instruction "With" qui entoure immédiatement [s'il n'y en a pas, ou si l'objet n'a pas de membrex
, alors.x
n'a pas de sens]. Oracle n'a rien inclus de la sorte en Java, mais une telle construction s'intégrerait parfaitement dans le langage.Cette technique est en fait utilisée dans le motif Builder.
Cependant, en général, il est évité car il est ambigu. Il n'est pas évident de savoir si la valeur de retour est l'objet (vous pouvez donc appeler d'autres paramètres), ou si l'objet de retour est la valeur qui vient d'être affectée (également un modèle commun). En conséquence, le principe de la moindre surprise suggère de ne pas essayer de supposer que l'utilisateur souhaite voir l'une ou l'autre solution, à moins que cela ne soit fondamental pour la conception de l'objet.
la source
Obj* x = doSomethingToObjAndReturnIt(new Obj(1, 2, 3));
me vient à l’esprit, c’est que je pense qu’il a également gagné en popularité parce que cela reflètea = b = c = d
bien, même si je ne suis pas convaincu que la popularité était bien fondée. J'ai vu celui que vous avez mentionné, renvoyant la valeur précédente, dans certaines bibliothèques d'opérations atomiques. Morale de l'histoire? C'est encore plus déroutant que je ne le pensais =)new BetterText(string).indent(4).box().print();
. Dans ce cas, j'ai contourné un tas de gobbledygook pour prendre une chaîne, la mettre en retrait, la mettre en boîte et la sortir. Vous pouvez même souhaiter une méthode de duplication au sein de la cascade (telle que, par exemple.copy()
) pour permettre à toutes les actions suivantes de ne plus modifier l'original.C'est plus un commentaire qu'une réponse, mais je ne peux pas en parler, alors ...
je voulais juste mentionner que cette question m'a surpris parce que je ne considère pas cela comme inhabituel du tout. En fait, dans mon environnement de travail (développeur web) est très commun.
Par exemple, voici comment la commande doctrine: generate: entity de Symfony génère tout automatiquement
setters
, par défaut .jQuery enchaîne un peu la plupart de ses méthodes de manière très similaire.
la source
JAB
. Au cours de mes années de collège , je l' habitude de regarder vers le bas à JS comme une abomination wanna-be langage de script, mais après avoir travaillé quelques années avec elle, et l' apprentissage de la droite façon de l' utiliser, je suis venu ... en fait aimer ce . Et je pense qu'avec ES6, cela va devenir un langage vraiment mature . Mais ce qui me fait tourner la tête, c’est… qu’est-ce que ma réponse a à voir avec JS en premier lieu? ^^ U