É um princípio básico, ou altamente desejável, ter métodos de classe que retornem "$ this" em vez de um valor?

8

Eu realmente comecei a aprender OOP. Comecei há cerca de um ano e escrevi provavelmente 15.000 linhas. Mas eu escrevi tudo com quase nenhuma experiência olhando para OOP de outras pessoas.

A maioria das funções da minha classe retornam um valor ou alteram as propriedades da classe e retornam verdadeiro / falso. Alguns dos que retornam um valor também salvam esse valor em uma propriedade de classe (após verificar primeiro se a propriedade de classe já está definida, o que pode evitar uma chamada ao banco de dados).

Agora estou pesquisando bastante no Magento / Zend e noto que muitos dos métodos de classe retornam "$ this". Pelo que entendi, diferentemente das funções regulares, os métodos de classe que retornam $ this atuam por referência, não por valor, para que você recupere o mesmo objeto com o qual iniciou, em vez de uma cópia.

Está retornando $thisalgo que eu deveria estar fazendo muito mais? Isso torna o código mais fácil de manter e usar? É $thisnecessário retornar para encadear métodos de classe?

Buttle Butkus
fonte
12
Esta é uma técnica empregada para criar interfaces fluentes. Se uma interface fluente não é seu objetivo, você não precisa retornar $this. en.wikipedia.org/wiki/Fluent_interface
Robert Harvey
1
Você pode querer especificar PHP aqui ...
Nathan C. Tresch
4
@ Robert, você deve fazer disso uma resposta.
Karl Bielefeldt
Aqui está um exemplo de interface fluente em PHP ( devzone.zend.com/777/fluent-interfaces-in-php )
OnesimusUnbound
2
Os métodos encadeados são necessários? no();Às vezes são muito convenientes? yes().yes().yes().yes().yes();: D
Viliam Búr

Respostas:

14

Esta é uma técnica empregada para criar interfaces fluentes. Se uma interface fluente não for seu objetivo, não será necessário devolver $ this.

Interfaces fluentes permitem escrever código parecido com este (pseudocódigo):

private void makeFluent(Customer customer) {
    customer.newOrder()
            .with(6, "TAL")
            .with(5, "HPK").skippable()
            .with(3, "LGV")
            .priorityRush();
}

Funciona porque cada chamada de método tem acesso ao objeto original em virtude do retorno da chamada de método anterior this. O código acima é mais ou menos equivalente a:

private void makeFluent(Customer customer) {
    var newOrder = customer.newOrder()
    newOrder.with(6, "TAL");
    newOrder.with(5, "HPK", skippable := true);
    newOrder.with(3, "LGV");
    newOrder.priorityRush();
    ...
}

thisnem sempre é retornado. Às vezes, um objeto diferente é retornado. O Linq funciona assim, retornando uma IEnumerable(uma representação lenta de uma coleção, um mecanismo de estado modificado, basicamente) de cada método Linq na cadeia.

Requer mais esforço e premeditação para criar uma interface fluente do que simplesmente definir todos os parâmetros em um construtor. Você precisa descobrir se o objeto tem tudo o que precisa para ser totalmente funcional, e nem sempre é claro exatamente o que um determinado método faz ou em que ordem você precisa chamar os métodos. Por exemplo:

20.minutes.ago

parece limpo, mas como funciona exatamente?

http://en.wikipedia.org/wiki/Fluent_interface
http://www.martinfowler.com/bliki/FluentInterface.html

Robert Harvey
fonte
2

Retornar $ this retorna uma instância do objeto do qual o método é membro. Existem muitos casos em que você deseja fazer isso, mas até entender exatamente o que acabei de dizer significa e poder identificar um caso de uso para ele, você realmente não precisa dele.

Exemplos de quando usar isso:

Um btree é um exemplo em PHP de onde você retornaria um filho de $ this, que não é exatamente a mesma coisa, mas acho que isso está relacionado a essa discussão.

Escrevendo seu próprio construtor, você sempre retornaria $ this

Para esclarecer as referências, quando você devolve $ this, está distribuindo uma referência ao objeto do qual o método em execução faz parte. Eu acho que as regras do PHP para quando ele está operando em uma referência versus um valor às vezes é um pouco obscuro, mas nesse caso $ isso é sempre uma referência. Para ser claro: uma referência é sempre 'a instância que você tinha' e nunca uma cópia, enquanto um 'valor' é uma cópia. Estes são dois tipos de semântica de argumentos

Nathan C. Tresch
fonte
Eu entendo Returning $this returns an instance of the object that the method is a member of.E no PHP OOP eu acredito, como eu disse na minha pergunta e você diz na sua resposta, que a instância que ela retorna é realmente a mesma que é ... ou seja, ela não retorna uma cópia de si mesma, mas de si mesma. Se estou entendendo corretamente.
Buttle Butkus
@ButtleButkus Sim, uma referência é sempre 'a instância que você tinha' e nunca uma cópia, enquanto um 'valor' é uma cópia. Esses são dois tipos de semântica de argumentos.
Nathan C. Tresch
@ButtleButkus I alterado a minha resposta com essa informação
Nathan C. Tresch
1

"Uma referência PHP é um alias, que permite que duas variáveis ​​diferentes gravem no mesmo valor. A partir do PHP 5, uma variável de objeto não contém mais o próprio objeto como valor. Ele contém apenas um identificador de objeto que permite que os acessadores de objeto encontre o objeto real. Quando um objeto é enviado por argumento, retornado ou atribuído a outra variável, as diferentes variáveis ​​não são apelidos: elas mantêm uma cópia do identificador, que aponta para o mesmo objeto ".

A vantagem real de retornar $ this está nos padrões de design da composição de objetos, especialmente no Magento. Tomemos, por exemplo, o Mage_Core_Sales_Order_Invoice-> capture () que retorna $ this. Este método se parece com:

  public function capture()
    {
        $this->getOrder()->getPayment()->capture($this);
        if ($this->getIsPaid()) {
            $this->pay();
        }
        return $this;
    }

a linha:

$this->getOrder()->getPayment()->capture($this);

Passa o modelo de fatura como referência, muito útil em padrões de composição de objetos. O retorno de $ this também é uma referência. Portanto, o método que chama -> capture () em um modelo de fatura pode continuar trabalhando com uma chamada de objeto encadeado a seguir para facilitar a leitura do código contextual.

mprototype
fonte