原型模式可以看作是工具复制中的一个主要内容。在学习原型模式时,我们理解到工具中的引用变量,也便是变量也是一个工具时,直接复制这个工具会导致个中的引用变量还是指向同一个工具。是不是有点绕,我们还是用例子来解释:
//clone方法classtestA{public$testValue;}classA{publicstatic$reference=0;public$instanceReference=0;public$t;publicfunction__construct(){$this->instanceReference=++self::$reference;$this->t=newtestA();}publicfunction__clone(){$this->instanceReference=++self::$reference;$this->t=newtestA();}}$a1=newA();$a2=newA();$a11=clone$a1;$a22=$a2;var_dump($a11);//$instanceReference,3var_dump($a22);//$instanceReference,2$a1->t->testValue='现在是a1';echo$a11->t->testValue,PHP_EOL;//''$a2->t->testValue='现在是a2';echo$a22->t->testValue,PHP_EOL;//现在是a2$a22->t->testValue='现在是a22';echo$a2->t->testValue,PHP_EOL;//现在是a22//利用clone后$a22=clone$a2;var_dump($a22);//$instanceReference,4$a2->t->testValue='现在是a2';echo$a22->t->testValue,PHP_EOL;//NULL$a22->t->testValue='现在是a22';echo$a2->t->testValue,PHP_EOL;//现在是a2
首先,通过变量的变革,我们可以看出利用clone关键字的工具复制会调用__clone()方法。这个魔术方法正在原型模式的核心所在。在这个方法中,我们可以重新实例化或者定义工具中的引用成员。通过clone,我们让$t变量重新实例化,从而让$t成为了新的工具,从而避免引用带来的问题。
在工具的复制中,我们须要特殊把稳的递归引用的问题。也便是工具内部引用了自身,将会导致来回的重复引用形成递归去世循环。
//循环引用问题classB{public$that;function__clone(){//Segmentationfault:11$this->that=clone$this->that;//$this->that=unserialize(serialize($this->that));//object(B)#6(1){//["that"]=>//object(B)#7(1){//["that"]=>//object(B)#8(1){//["that"]=>//RECURSION无限递归//}//}//}}}$b1=newB();$b2=newB();$b1->that=$b2;$b2->that=$b1;$b3=clone$b1;var_dump($b3);
B类中的that指向自身的实例,两个工具相互指向后再进行复制,就会涌现这种去世循环的情形。利用序列化和反序列化输出后,我们会看到RECURSION的引用提示。这便是形成了递归的去世循环。这种情形一定要极力避免。
上述例子中,我们利用了序列化和反序列化这一招来办理引用问题。工具复制的工具变量来说(工具变量里面还有更多层次的引用变量),这种办法能够一次性地在最顶层的工具__clone()方法中办理引用问题。
测试代码: https://github.com/zhangyue0503/dev-blog/blob/master/php/202001/source/%E5%85%B3%E4%BA%8EPHP%E4%B8%AD%E5%AF%B9%E8%B1%A1%E5%A4%8D%E5%88%B6%E7%9A%84%E9%82%A3%E7%82%B9%E4%BA%8B%E5%84%BF.php
参考文档: https://www.php.net/manual/zh/language.oop5.cloning.php