Quando usar encadeamento
O encadeamento de funções é popular principalmente em idiomas em que um IDE com preenchimento automático é um local comum. Por exemplo, quase todos os desenvolvedores de C # usam o Visual Studio. Portanto, se você estiver desenvolvendo com C #, adicionar encadeamento a seus métodos pode economizar tempo para os usuários dessa classe, pois o Visual Studio o ajudará na criação da cadeia.
Por outro lado, linguagens como PHP, que são altamente dinâmicas por natureza e geralmente não têm suporte de preenchimento automático em IDEs, terão menos classes que suportam encadeamento. O encadeamento somente será apropriado quando phpDocs corretos forem empregados para expor os métodos encadeados.
O que é encadeamento?
Dada uma classe denominada, Foo
os dois métodos a seguir são encadeados.
function what() { return this; }
function when() { return new Foo(this); }
O fato de ser uma referência à instância atual e criar uma nova instância não altera que esses sejam métodos encadeados.
Não há regra de ouro de que um método encadeado possa fazer referência apenas ao objeto atual. Os métodos encadeados e de fato podem ser divididos em duas classes diferentes. Por exemplo;
class B { function When() { return true; } };
class A { function What() { return new B(); } };
var a = new A();
var x = a.What().When();
Não há referência a this
nenhum dos exemplos acima. O código a.What().When()
é um exemplo de encadeamento. O interessante é que o tipo de classe B
nunca é atribuído a uma variável.
Um método é encadeado quando seu valor de retorno é usado como o próximo componente de uma expressão.
Aqui estão mais alguns exemplos
// return value never assigned.
myFile.Open("something.txt").Write("stuff").Close();
// two chains used in expression
int x = a.X().Y() * b.X().Y();
// a chain that creates new strings
string name = str.Substring(1,10).Trim().ToUpperCase();
Quando usar this
enew(this)
As strings na maioria dos idiomas são imutáveis. Portanto, as chamadas de método de encadeamento sempre resultam na criação de novas strings. Onde como um objeto como StringBuilder pode ser modificado.
Consistência é a melhor prática.
Se você possui métodos que modificam o estado de um objeto e retornam this
, não misture métodos que retornam novas instâncias. Em vez disso, crie um método específico chamado Clone()
que fará isso explicitamente.
var x = a.Foo().Boo().Clone().Foo();
Isso é muito mais claro quanto ao que está acontecendo lá dentro a
.
A etapa fora e truque traseiro
Eu chamo isso de truque passo a passo , porque resolve muitos problemas comuns relacionados ao encadeamento. Basicamente, significa que você sai da classe original para uma nova classe temporária e depois volta para a classe original.
A classe temporária existe apenas para fornecer recursos especiais à classe original, mas somente sob condições especiais.
Muitas vezes, uma cadeia precisa mudar de estado , mas a classe A
não pode representar todos esses estados possíveis . Portanto, durante uma cadeia, é introduzida uma nova classe que contém uma referência para A
. Isso permite que o programador entre em um estado e volte para A
.
Aqui está o meu exemplo, que o estado especial seja conhecido como B
.
class A {
function Foo() { return this; }
function Boo() { return this; }
function Change() return new B(this); }
}
class B {
var _a;
function (A) { _a = A; }
function What() { return this; }
function When() { return this; }
function End() { return _a; }
}
var a = new A();
a.Foo().Change().What().When().End().Boo();
Agora esse é um exemplo muito simples. Se você quiser ter mais controle, B
poderá retornar a um novo supertipo A
que possui métodos diferentes.
return this
. Como posso ajudar os usuários de minhas bibliotecas a lidar e entender essa situação? (Permitindo-lhes métodos cadeia, mesmo quando não é necessário, se isso realmente ficar bem Ou deveria eu só ficar com uma forma, exigem mudança em tudo?)Dependendo do idioma, ter métodos que retornem
void
/unit
e modifiquem sua instância ou parâmetros não é idiomático. Mesmo nas linguagens que costumavam fazer isso mais (C #, C ++), está saindo de moda com a mudança para uma programação de estilos mais funcionais (objetos imutáveis, funções puras). Mas vamos assumir que há uma boa razão por enquanto.Para certos comportamentos (pense
x++
), espera-se que a operação retorne o resultado, mesmo que modifique a variável. Mas essa é a única razão para fazê-lo por conta própria.Depende.
Nos idiomas em que copiar / novo e retornar são comuns (C # LINQ e seqüências de caracteres), retornar a mesma referência seria confuso. Nas linguagens em que modificar e retornar é comum (algumas bibliotecas C ++), a cópia seria confusa.
Tornar a assinatura inequívoca (retornando
void
ou usando construções de linguagem como propriedades) seria a melhor maneira de esclarecer. Depois disso, deixar claro o nomeSetFoo
para mostrar que você está modificando a instância existente é bom. Mas a chave é manter os idiomas da linguagem / biblioteca com a qual você está trabalhando.fonte
Clear()
ouAdd()
de qualquer tipo de coleção modificam a mesma instância e retornamvoid
. Em ambos os casos você diz que seria confuso ...obj = myCollection.Where(x => x.MyProperty > 0).OrderBy(x => x.MyProperty);
então?Where()
retorna um novo objeto de coleção.OrderBy()
até retorna um objeto de coleção diferente, porque o conteúdo é ordenado.IEnumerable
, mas para ser claro, eles não são cópias, e eles não são coleções (excetoToList
,ToArray
, etc.).ICollection
. Foi mal.(Eu assumi C ++ como sua linguagem de programação)
Para mim, esse é principalmente um aspecto de legibilidade. Se A, B, C são modificadores, especialmente se este é um objeto temporário passado como parâmetro para alguma função, por exemplo
comparado com
Regenerando se está correto retornar uma referência à instância modificada, eu diria que sim e aponto, por exemplo, para os operadores de fluxo '>>' e '<<' ( http://www.cprogramming.com/tutorial/ operator_overloading.html )
fonte
Você também pode executar o encadeamento do método com retorno de cópia em vez de modificar o retorno.
Um bom exemplo de C # é string.Replace (a, b), que não altera a string na qual foi invocada, mas retorna uma nova string que permite que você se encaminhe alegremente.
fonte