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是不依赖于数据库的独立数据集合。
——百度百科