如何在链式调用方法时克隆对象实例?
你的问题出在链式调用返回的是当前对象的引用,而不是新的实例。
当你执行$x1 = $x->set()时,set()方法返回的是$x本身(return $this),所以$x1和$x指向的是同一个xyz对象实例。后续调用$x->change($args)时,修改的是这个共享实例里的$foo变量,自然$x1->show()和$x2->show()都会输出修改后的值——因为它们本质上都是在操作同一个对象。
之前的$x->set()->show()和$x->change($args)->show()看起来正常,是因为你是连续修改同一个对象的状态并立即输出,没有保留对中间状态的引用。
要让每个链式调用分支持有独立的对象实例,你需要让set()和change()方法返回新的对象实例,而不是当前对象的引用。这里有两种常用实现方式:
方式1:创建全新实例
每次调用方法时,生成一个新的xyz对象,设置好$foo后返回这个新实例:
class xyz { private $foo; public function set() { $newObj = new self(); $newObj->foo = new Foo(); return $newObj; } public function change($args) { $newObj = new self(); $newObj->foo = new Foo($args); return $newObj; } public function show() { return $this->foo->print(); } }
这样$x1 = $x->set()得到的是一个独立的新实例,$x2 = $x->change($args)是另一个独立实例,修改其中一个不会影响另一个。
方式2:克隆当前对象
如果你的xyz类还有其他属性需要保留原对象的状态,克隆当前对象是更合适的选择。克隆后修改克隆体的$foo,再返回克隆体:
class xyz { private $foo; public function set() { $clone = clone $this; $clone->foo = new Foo(); return $clone; } public function change($args) { $clone = clone $this; $clone->foo = new Foo($args); return $clone; } public function show() { return $this->foo->print(); } }
这种方式会复制原对象的所有属性,只修改$foo,适合需要基于原对象状态创建变体的场景。
你原来的链式调用属于可变对象模式,对象状态会被原地修改,所有指向该对象的变量都会同步看到变化。而上面的解决方案属于不可变对象模式的链式调用——每次修改操作都会返回一个新的对象,原对象状态保持不变,这样就能保证每个引用都持有独立的状态。
内容的提问来源于stack exchange,提问作者DG_

