What is method chaining?

14

In object-oriented languages, there is a concept known as method chaining or encadeamento de métodos to Portuguese.

  

What exactly is this?

     

How would you implement this technique in a PHP application?

    
asked by anonymous 23.12.2015 / 07:57

1 answer

17

TL; DR

Method chaining is a technique that makes it possible to execute several methods on an object within the same statement ( usually separated by semicolons ).

Such methods, in general, have some side effect and do not return meaningful values.

How it works

Normally you execute several methods of an object in the following way:

class Classe {
    public function executar_isto() {
        //faz algo importante
    }
    public function executar_aquilo() {
        //faz algo mais importante
    }
}
$objeto = new Classe();
$objeto->executar_isto();
$objeto->executar_aquilo();

Note that these methods do not return a value.

However, if calling multiple methods is common on this object, you can rewrite them so that they return $this and calls can be chained:

class Classe {
    public function executar_isto() {
        //faz algo importante
        return $this;
    }
    public function executar_aquilo() {
        //faz algo mais importante
        return $this;
    }
}
$objeto = new Classe();
$objeto->executar_isto()->executar_aquilo();

It may seem confusing at first, but once it is understood that calls are always made to the same object, which is returned by each method, you realize that this is actually easier to read and "clean" than to repeat the object several times.

Note that the various methods are now executed within a single statement, ie no need to break calls on multiple calls separated by semicolons. This allows you to perform inline operations, for example in method parameters and makes the code less verbose :

$outro_objeto->algum_metodo_legal(
        $objeto->executar_isto()->executar_aquilo(),
        //outros parâmetros
    );

Fluent interfaces and builder pattern

Method chaining alone is not very attractive. However, if used with other patterns like fluent interfaces and builder pattern , the result starts to look very interesting.

If you want an additional reading, I have an article called Constructing objects intelligently: Builder Pattern and Fluent Interfaces on the subject, but with some examples in Java.

Fluent interfaces

Basically, fluent interfaces consist of threaded methods whose names are meaningful for a given operation. For example:

$aviao->abastecer()->decolar()->voarPara("Disney")->pousar();

Some APIs go further and create a Domain Specific Languages > such as, for example, the PDO library with which you can do: / p>

$data = $pdo->query('SELECT * FROM TABELA')->fetchAll();

Builder pattern

Another application of method chaining is in object building. I did a small example on Ideone to illustrate.

Suppose your online store has a shopping cart with items, represented by the following class:

class Cesta {
    private $itens;
    public function __construct(array $itens) {
        $this->itens = $itens;
    }
    public function show() {
        echo "Minha cesta:\n";
        foreach ($this->itens as $item) {
            echo $item."\n";
        }
    }
}

Note that the implementation of Cesta is immutable, that is, once the object is built, it can no longer be changed. There are several advantages to this, but I will not go into detail here. The idea is that you need to pass all items at once.

To facilitate the construction of Cesta , let's implement a builder :

class CestaBuilder {
    private $itens = array();
    static public function create() {
        return new static;
    }
    public function adicionar($item) {
        $this->itens[] = $item;
        return $this;
    }
    public function build() {
        return new Cesta($this->itens);
    }
}

Our builder allows you to compose the items in their own instance and at some point the build method is called to return an instance of Cesta with the collected items.

Example usage:

$minha_cesta = CestaBuilder::create()
    ->adicionar("Pão")
    ->adicionar("Queijo")
    ->adicionar("Mortadela")
    ->build();

Considerations

Again, this may all seem a bit confusing at first, but once you understand these concepts well, you'll hardly want to use something else.

Using concatenated methods, fluent interfaces, and builders makes your code more intuitive and clean.

In the case of an API, you avoid having to keep looking through the documentation for what methods to call, since often the self-completion of IDEs already shows the possibilities of using the class. This becomes more evident once you get used to the use of standards, because when using new APIs you kind of already know what to expect from them.

What is not method chaining

There is a "false" way to chain methods, which is actually a bad practice. It consists of calling several methods in sequence, apparently in the same way, but actually accessing several different objects.

For example:

$contato = $empresa->getProjeto("foo")->getFuncionario("bar")->getNome();

Although the above code is intuitive and compact, it brings risks that a good design should not bring.

Each method returns a different object, so it may be that in some call the object is not found and null is returned. An error will occur without treatment.

In addition, you should generally avoid having any code that has knowledge about various levels of objects, as this greatly increases the coupling.

The alternative in this case is to create a method in Empresa to return the desired information. Example:

$contato = $empresa->getNomeContatoPorProjeto("foo","bar");
    
24.12.2015 / 01:11