Method chaining: Difference between revisions

Content deleted Content added
Wycats (talk | contribs)
Added POV dispute
 
(33 intermediate revisions by 28 users not shown)
Line 1:
{{Short description|Programming syntax}}
{{refimprove|date=May 2008}}
 
{{POV|date=November 2017}}
'''Method chaining''', also known as '''named parameter idiom''', is a common [[Syntax (programming languages)|syntax]] for invoking multiple method calls in [[Object-oriented programming|object-oriented programming languages]]. Each method returns an object, allowing the calls to be chained together in a single statement without requiring variables to store the intermediate results.<ref>{{cite web
| accessdate = 2011-04-13
|publisher = First Class Thoughts
| ___location = http://firstclassthoughts.co.uk/
|title = Applying Method Chaining
| publisher = First Class Thoughts
|quote = In order to simplify repeated object interactions on the same object the old trick ''Method Chaining'' originating the world of Smalltalk should be enforced. The idea is to let methods return<code> this </code>rather than<code> void</code>, thus affecting especially<code> set() </code>and<code> add() </code>methods. Method chaining arose during the designers of Smalltalk pursuit to minimize the number of keywords in the language, which lead to the discovery that<code> void </code>is an unnecessary keyword!.
| title = Applying Method Chaining
|url = http://firstclassthoughts.co.uk/java/method_chaining.html
| quote = In order to simplify repeated object interactions on the same object the old trick Method Chaining originating the world of Smalltalk should be enforced. The idea is to let methods return this rather than void, thus affecting especially set() and add() methods. Method chaining arose during the designers of Smalltalk pursuit to minimize the number of keywords in the language, which lead to the discovery that void is an unnecessary keyword!.
|url-status = dead
| url = http://firstclassthoughts.co.uk/java/method_chaining.html}}</ref> [[Local variable]] declarations are [[syntactic sugar]] because of the difficulty humans have with deeply nested method calls.<ref>{{cite web|url=http://cs.uni.edu/~wallingf/teaching/cs3540/sessions/session18.html|title=Session 18 Variable References|quote=Today you learn that variable names are not necessary: they are really syntactic sugar.}}</ref><ref>{{cite web|url=https://www.cs.umd.edu/class/spring2013/cmsc631/lectures/lambda.pdf|title=CMSC 631 – Program Analysis and Understanding|quote=• Syntactic sugar for local declarations - let x = e1 in e2 is short for (λx.e2) e1}}</ref> A method chain is also known as a ''train wreck'' due to the increase in the number of methods that come one after another in the same line that occurs as more methods are chained together<ref>{{cite book
|archiveurl = https://web.archive.org/web/20110222112016/http://firstclassthoughts.co.uk/java/method_chaining.html
|archivedate = 2011-02-22
}}</ref>
 
== Rationale ==
 
[[Local variable]] declarations are [[syntactic sugar]].<ref>{{cite web|url=https://www.cs.umd.edu/class/spring2013/cmsc631/lectures/lambda.pdf|title=CMSC 631 – Program Analysis and Understanding|quote=• Syntactic sugar for local declarations - let x = e1 in e2 is short for (λx.e2) e1}}</ref>
Method chaining eliminates an extra variable for each intermediate step. The developer is saved from the cognitive burden of naming the variable and keeping the variable in mind.
 
Method chaining has been referred to as producing a "train wreck" due to the increase in the number of methods that come one after another in the same line that occurs as more methods are chained together.<ref>{{cite book
| last = Martin
| first = Robert Cecil
Line 14 ⟶ 26:
| publisher = [[Prentice Hall]]
| year = 2008
| isbn = 978-0-13-235088-24
}}</ref>
}}</ref> even though [[newline|line breaks]] are often added between methods.
 
A similar syntax is [[method cascading]], where after the method call the expression evaluates to the current object, not the [[return value]] of the method. Cascading can be implemented using method chaining by having the method return the [[this (computer programming)|current object itself]]. Cascading is a key technique in [[fluent interface]]s, and since chaining is widely implemented in object-oriented languages while cascading isn't, this form of "cascading-by-chaining by returning <tt>{{mono|this</tt>}}" is often referred to simply as "chaining". Both chaining and cascading come from the [[Smalltalk]] language.
 
While chaining is syntax, it has semantic consequences, namely that requires methods to return an object, and if implementing cascading via chaining, this must be the current object. This prevents the return value from being used for some other purpose, such as returning an [[error value]].
 
== Examples ==
A common example is [[iostream]] in [[C++]], where for example <code>&lt;&lt;</code> returns the left object, allowing chaining.
 
Compare:
=== Scala ===
<syntaxhighlight lang="cpp">
A paradigm in functional programming is immutability in method chaining
a << b << c;
<source lang="scala">
</syntaxhighlight>
case class Person(private val name: String = null , private val age: Int = 0 ) {
equivalent to:
def setName(newName: String) = Person( newName, this.age )
<syntaxhighlight lang="cpp">
def setAge(newAge: Int) = Person( this.name, newAge )
a << b;
def introduce { println( s"Hello, my name is $name and I am $age years old." ) }
a << c;
}
</syntaxhighlight>
 
Another example in [[JavaScript]] uses the built-in methods of Array:
object App {
<syntaxhighlight lang="javascript">
def main(args: Array[String]) {
const interesting_products = products
// Output: Hello, my name is Peter and I am 21 years old.
.filter(x => x.count > 10)
Person().setName("Peter").setAge(21).introduce
.sort((a, b) => a.count - b.count)
}
.map(x => x.name)
}
</syntaxhighlight>
</source>
 
Note that in JavaScript <code>filter</code> and <code>map</code> return a new shallow copy of the preceding array but <code>sort</code> operates in place. To get a similar behavior, <code>toSorted</code> may be used. But in this particular case, <code>sort</code> operates on the new array returned from <code>filter</code> and therefore does not change the original array.
=== Java ===
The following is an example in [[Java (programming language)|Java]] of how method chaining might be implemented and used:
<source lang="java">
class Person {
private String name;
private int age;
 
// In addition to having the side-effect of setting the attributes in question,
// the setters return "this" (the current Person object) to allow for further chained method calls.
 
public Person setName(String name) {
this.name = name;
return this;
}
 
public Person setAge(int age) {
this.age = age;
return this;
}
 
public void introduce() {
System.out.println("Hello, my name is " + name + " and I am " + age + " years old.");
}
 
// Usage:
public static void main(String[] args) {
Person person = new Person();
// Output: Hello, my name is Peter and I am 21 years old.
person.setName("Peter").setAge(21).introduce();
}
}
</source>
 
By contrast, here is a non-chained equivalent:
<source lang="java">
class Person {
private String name;
private int age;
 
// Per normal Java style, the setters return void.
 
public void setName(String name) {
this.name = name;
}
 
public void setAge(int age) {
this.age = age;
}
 
public void introduce() {
System.out.println("Hello, my name is " + name + " and I am " + age + " years old.");
}
 
// Usage:
public static void main(String[] args) {
Person person = new Person();
// Not using chaining; longer than the chained version above.
// Output: Hello, my name is Peter and I am 21 years old.
person.setName("Peter");
person.setAge(21);
person.introduce();
}
}
</source>
 
=== jQuery ===
[[jQuery]] relies heavily on chaining. This makes it easy to call several methods on the same selection. It also makes code more clear and prevents executing the same selection several times (hence improving performance).
The following code exemplifies only its usage (and not its implementation which is in charge of jQuery):
<source lang="javascript">
// chaining
$("#person").slideDown('slow')
.addClass('grouped')
.css('margin-left', '11px');
</source>
 
<source lang="javascript">
// no chaining
var p = $('#person');
p.slideDown('slow');
p.addClass('grouped');
p.css('margin-left', '11px');
</source>
 
===C++===
Method chaining can be expressed in C++ as follows:
 
<source lang="cpp">
#include <string>
#include <iostream>
using namespace std;
 
class Person
{
string m_name;
int m_age;
 
public:
Person &name(string const &name) {this->m_name = name; return *this;}
Person &age(int const age) {this->m_age = age; return *this;}
 
friend ostream & operator << (ostream &os, Person const &);
};
 
ostream & operator << (ostream &os, Person const &person)
{
return os << "Hello, my name is " << person.m_name << " and I am " << person.m_age << " years old.";
}
 
int main(void)
{
Person person;
cout << person.name("Peter").age(21) << endl;
 
return 0;
}
</source>
 
=== ObjectPascal (Delphi) ===
Method chaining can be expressed in ObjectPascal as follows:
 
<source lang="delphi">
interface
TPerson = class
private
FName: string;
FAge: Integer;
public
function name(const aName: string): TPerson;
function age(const anAge: Integer): TPerson;
function introduce(): TPerson;
end;
 
implementation
 
function TPerson.age(const anAge: Integer): TPerson;
begin
self.FAge := anAge;
Result := self;
end;
 
 
 
function TPerson.introduce: TPerson;
begin
ShowMessage(Format('Hello, my name is %s and I am %d years old.', [FName, FAge]));
Result := self;
end;
 
 
function TPerson.name(const aName: string): TPerson;
begin
self.FName := aName;
Result := self;
end;
 
 
initialization
TPerson.Create().name('Mohamed').age(49).introduce().Free;
 
end.
 
</source>
 
===Ruby===
In Ruby, method chaining can be expressed as follows:
 
<source lang="ruby">
class Person
def name(value)
@name = value
self
end
def age(value)
@age = value
self
end
def introduce
puts "Hello, my name is #{@name} and I am #{@age} years old."
end
end
 
person = Person.new
person.name("Peter").age(21).introduce
# => Hello, my name is Peter and I am 21 years old.
 
</source>
 
=== PHP ===
Implementation and usage of method chaining in [[PHP]]:
 
<source lang="php">
<?php
 
class Person {
 
protected $name;
protected $age;
 
public function setName($name) {
$this->name = $name;
return $this;
}
 
public function setAge($age) {
$this->age = $age;
return $this;
}
 
public function __toString() {
return "Hello, my name is " . $this->name . " and I am " . $this->age . " years old.";
}
}
 
$person = new Person;
echo $person->setName("Peter")->setAge(21); // echo on object automatically calls magic method __toString()
 
</source>
 
=== Python ===
<source lang="python">
class Person:
def name(self, value):
self.name = value
return self
def age(self, value):
self.age = value
return self
def introduce(self):
print "Hello, my name is", self.name, "and I am", self.age, "years old."
person = Person()
person.name("Peter").age(21).introduce()
# => Hello, my name is Peter and I am 21 years old.
</source>
 
===Io===
<source lang="io">
Person := Object clone do(
# ::= operator creates a setter which can be used in method chaining
name ::= "Unknown"
age ::= 0
introduce := method(
"Hello, my name is #{self name} and I'm #{self age} years old" interpolate println
)
)
 
person := Person clone
person setName("Peter") setAge(21) introduce
 
</source>
 
==See also==
Line 302 ⟶ 61:
* [[Nesting (computing)]]
* [[Builder pattern]]
* [[Pyramid of doom (programming)]]
 
==References==
Line 307 ⟶ 67:
 
==External links==
* [http://www.infoq.com/articles/internal-dsls-java Creating DSLs in Java using method chaining concept]
* [https://programmingdive.com/method-chaining-in-php/ Method Chaining in PHP]
 
{{Design patterns}}
 
{{DEFAULTSORT:Method Chaining}}