9.3原型模式
原型模式(Prototype),用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式(Prototype)结构图
原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。
代码示例:
原型类
bstract class Prototype{private $id = '';public function __construct($id){$this->id = $id;}public function getID(){return $this->id;}//抽象类关键就是有这样一个Clone方法abstract function copy();}
具体原型类
class ConcretePrototype1 extends Prototype{public function __construct($id){parent::__construct($id);}//创建当前对象的浅表副本。方法是创建一个新对象,//然后将当前对象的非静态字段复制到该新对象。如//果字段是值类型的,则对该字段执行逐位复制。如//果字段是引用类型,则复制引用但不复制引用的对//象;因此,原始对象及其副本引用同一对象。public function copy(){return clone $this;}}
客户端代码
public function prototypeDemo(){$p1 = new ConcretePrototype1('I');//克隆类Concrete Prototype1的对象$p1就能//得到新的实例$c1$c1 = $p1->copy();echo $c1->getID();}
9.4简历的原型实现
结构图
代码实现
abstract class ICloneable{abstract function copy();}
简历类
class Resume extends ICloneable{private $name = '';private $sex = '';private $age = '';private $timeArea = '';private $company = '';public function __construct(string $name){$this->name = $name;}public function setPersonalInfo(string $sex, string $age){$this->sex = $sex;$this->age = $age;}public function setWorkExperience(string $timeArea, string $company){$this->timeArea = $timeArea;$this->company = $company;}public function toShow(){echo $this->name . ' ' . $this->sex . ' ' . $this->age . PHP_EOL;echo '工作经历:' . $this->timeArea . ' ' . $this->company . PHP_EOL;}//实现接口的方法,用来克隆对象public function copy(){return clone $this;}}
客户端代码
public function prototypeImp(){$a = new Resume('大鸟');$a->setPersonalInfo('男', '29');$a->setWorkExperience('1998-2000', 'XX公司');//只需要调用Clone方法就可以实现新简历的生成,//并且可以再修改新简历的细节$b = $a->copy();$b->setWorkExperience('1998-2006', 'YY企业');$c = $a->copy();$c->setPersonalInfo('男', '24');$a->toShow();$b->toShow();$c->toShow();}
一般在初始化的信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,又对性能是大大的提高。
不用重新初始化对象,而是动态地获得对象运行时的状态。
9.5浅复制与深复制
当前克隆方法是这样,如果字段是值类型的,则对该字段执行逐位复制,如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。
结构图
代码实现:
工作经历类
class WorkExperience{private $workDate = '';private $company = '';public function setWorkInfo(string $workDate, string $company){$this->workDate = $workDate;$this->company = $company;}public function getWorkDate(){return $this->workDate;}public function getCompany(){return $this->company;}}
简历类
class Resume extends ICloneable{private $name = '';private $sex = '';private $age = '';private $work = null;public function __construct(string $name){$this->name = $name;$this->work = new WorkExperience();}public function setPersonalInfo(string $sex, string $age){$this->sex = $sex;$this->age = $age;}public function setWorkExperience(string $timeArea, string $company){$this->work->setWorkInfo($timeArea, $company);}public function toShow(){echo $this->name . ' ' . $this->sex . ' ' . $this->age . PHP_EOL;echo '工作经历:' . $this->work->getWorkDate() . ' ' . $this->work->getCompany() . PHP_EOL;}public function copy(){return clone $this;}}
客户端代码
public function prototypeShallow(){$a = new ResumeShallow("大鸟");$a->setPersonalInfo('男', '29');$a->setWorkExperience('1998-2000', 'XX公司');$b = $a->copy();$b->setWorkExperience('1998-2006', 'YY企业');$c = $a->copy();$c->setPersonalInfo('男', '24');$c->setWorkExperience('1998-2003', 'ZZ企业');$a->toShow();$b->toShow();$c->toShow();}
输出结果
//三次输出结果都是最后一次设置的值大鸟 男 29工作经历:1998-2003 ZZ企业大鸟 男 29工作经历:1998-2003 ZZ企业大鸟 男 24工作经历:1998-2003 ZZ企业
‘浅复制’,被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。但我们可能更需要这样的一种需求,把要复制的对象所引用的对象都复制一遍。比如刚才的例子,我们希望是a、b、c三个引用的对象都是不同的,复制时就一变二,二变三,此时,我们就叫这种方式为‘深复制’,深复制把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。
深复制要深入到多少层,需要事先就考虑好,而且要当心出现循环引用的问题,需要小心处理,这里比较复杂,可以慢慢研究。
Tips:待完善PHP的浅复制与深复制
9.6简历的深复制实现
结构图
代码实现
工作经历类
class WorkExperience/* extends ICloneable*/{private $workDate = '';private $company = '';public function setWorkInfo(string $workDate, string $company){$this->workDate = $workDate;$this->company = $company;}public function getWorkDate(){return $this->workDate;}public function getCompany(){return $this->company;}// public function copy()// {// return clone $this;/*unserialize(serialize($this));*/// }}
简历类
class Resume extends ICloneable{private $name = '';private $sex = '';private $age = '';private $work = null;public function __construct(string $name){$this->name = $name;$this->work = new WorkExperience();// $this->work = $work->copy();}public function setPersonalInfo(string $sex, string $age){$this->sex = $sex;$this->age = $age;}public function setWorkExperience(string $timeArea, string $company){$this->work->setWorkInfo($timeArea, $company);}public function toShow(){echo $this->name . ' ' . $this->sex . ' ' . $this->age . PHP_EOL;echo '工作经历:' . $this->work->getWorkDate() . ' ' . $this->work->getCompany() . PHP_EOL;}public function copy(){return unserialize(serialize($this));}}
客户端代码
public function prototypeDeep(){$a = new ResumeDeep('大鸟');$a->setPersonalInfo('男', '31');$a->setWorkExperience('1998-2001', 'XX公司');$b = $a->copy();$b->setWorkExperience('1998-2006', 'YY企业');$c = $a->copy();$c->setPersonalInfo('男', '24');$c->setWorkExperience('1998-2003', 'ZZ企业');$a->toShow();$b->toShow();$c->toShow();}
输出结果
大鸟 男 31工作经历:1998-2001 XX公司大鸟 男 31工作经历:1998-2006 YY企业大鸟 男 24工作经历:1998-2003 ZZ企业
在一些特定场合,会经常涉及深复制或浅复制,比如说,数据集对象DataSet,它就有Clone()方法和Copy()方法,Clone()方法用来复制DataSet的结构,但不复制DataSet的数据,实现了原型模式的浅复制。Copy()方法不但复制结构,也复制数据,其实就是实现了原型模式的深复制。
DataSet是ADO.NET的中心概念。可以把DataSet当成内存中的数据库,DataSet是不依赖于数据库的独立数据集合。
——百度百科
