What is a chain of methods?

Asked

Viewed 2,146 times

17

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 be the implementation of this technique in a PHP application ?

1 answer

23


TL;DR

Chaining of methods is a technique that makes it possible to execute several methods on an object within the same instruction (usually separated by semicolon).

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

How it works

Normally you run several methods of an object as follows:

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 value.

However, if the call of various methods is common in this object, you can rewrite them so that they return $this and calls can be linked:

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 the calls are always made on the same object, which is returned by each method, you notice that this is actually easier to read and "clean" than repeating the object several times.

Note that the various methods are now executed within a single instruction, that is, without the need to break the calls into several calls separated by semicolons. This allows to perform operations inline, 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

The chaining of methods alone is not very attractive. However, if used with other standards like Fluent interfaces and Builder Pattern, the result starts to get very interesting.

If you’d like an additional reading, I have an article called Intelligently Building Objects: Pattern Builder and Fluent Interfaces on the subject, but with some examples in Java.

Fluent interfaces

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

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

Some Apis go further and create almost one DSL (Domain Specific Languages), as is, for example, the case of the library PDO, with which it is possible to:

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

Builder Pattern

Another application of method chaining is in the construction of objects. I made a small example in Ideone to illustrate.

Suppose your virtual store has a shopping basket 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 built the object, 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 the items at once.

To facilitate the construction of the Cesta, let’s implement a calsse 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 the composition of items in their own instance and at some point the method build is called to return an instance of Cesta with the collected items.

Example of use:

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

Considerations

Again, all this may seem a little confusing at first, but once you understand such concepts well, you will hardly want to use anything else.

The use of chained methods, fluent interfaces and Builders makes your code cleaner and more intuitive.

In the case of an API, it avoids you having to stare at every moment in the documentation looking for what methods to call, since often the auto-complete of the Ides already shows the possibilities of using the class. This is most evident once you get used to using the patterns, because by using new Apis you kind of already know what to expect from them.

What is not methods chaining

There is a "false" way of chaining methods, which is actually 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 carries risks that a good design shouldn’t 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 possibility of treatment.

In addition, in general one should avoid that any code has knowledge about various levels of objects, as this greatly increases the coupling.

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

$contato = $empresa->getNomeContatoPorProjeto("foo","bar");
  • Great @utluiz response, but there was a little doubt in my head about it? I was told that chained methods recreate the object instance, that’s true? Obs: I didn’t create a new question for that, so you don’t get it as a duplicate.

  • @Ivanferrer Depends. In my example Builder returns the instance itself, so there is no instance creation. This is because Builder is a transient class and may have its status changed. However, if you work with immutable objects, then each call to some method can create another instance, which returns a new immutable object with new value. I don’t know of any example in PHP so face, but a classic example is Java Bigdecimal.

  • Hi @utluiz, thank you so much for the return and for clarifying my question!

  • no create() you put "Return new Static", I could instantiate the object inside the function and return the object? Type "$obj = new Cestabuilder(); Return $obj". Is a bad practice?

  • 1

    @ivanveloso Yes, new CestaBuilder would work. To tell you the truth, it’s been so long that I can’t remember why I used the new static - who uses late Binding and it’s important in the case of inheritance - in this example. It was probably something I was working with and ended up getting.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.