9.3原型模式

原型模式(Prototype),用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式(Prototype)结构图 第九章 简历复印——原型模式 - 图1原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。
代码示例:
原型类

  1. bstract class Prototype
  2. {
  3. private $id = '';
  4. public function __construct($id)
  5. {
  6. $this->id = $id;
  7. }
  8. public function getID()
  9. {
  10. return $this->id;
  11. }
  12. //抽象类关键就是有这样一个Clone方法
  13. abstract function copy();
  14. }

具体原型类

  1. class ConcretePrototype1 extends Prototype
  2. {
  3. public function __construct($id)
  4. {
  5. parent::__construct($id);
  6. }
  7. //创建当前对象的浅表副本。方法是创建一个新对象,
  8. //然后将当前对象的非静态字段复制到该新对象。如
  9. //果字段是值类型的,则对该字段执行逐位复制。如
  10. //果字段是引用类型,则复制引用但不复制引用的对
  11. //象;因此,原始对象及其副本引用同一对象。
  12. public function copy()
  13. {
  14. return clone $this;
  15. }
  16. }

客户端代码

  1. public function prototypeDemo()
  2. {
  3. $p1 = new ConcretePrototype1('I');
  4. //克隆类Concrete Prototype1的对象$p1就能
  5. //得到新的实例$c1
  6. $c1 = $p1->copy();
  7. echo $c1->getID();
  8. }

9.4简历的原型实现

结构图 第九章 简历复印——原型模式 - 图2代码实现

  1. abstract class ICloneable
  2. {
  3. abstract function copy();
  4. }

简历类

  1. class Resume extends ICloneable
  2. {
  3. private $name = '';
  4. private $sex = '';
  5. private $age = '';
  6. private $timeArea = '';
  7. private $company = '';
  8. public function __construct(string $name)
  9. {
  10. $this->name = $name;
  11. }
  12. public function setPersonalInfo(string $sex, string $age)
  13. {
  14. $this->sex = $sex;
  15. $this->age = $age;
  16. }
  17. public function setWorkExperience(string $timeArea, string $company)
  18. {
  19. $this->timeArea = $timeArea;
  20. $this->company = $company;
  21. }
  22. public function toShow()
  23. {
  24. echo $this->name . ' ' . $this->sex . ' ' . $this->age . PHP_EOL;
  25. echo '工作经历:' . $this->timeArea . ' ' . $this->company . PHP_EOL;
  26. }
  27. //实现接口的方法,用来克隆对象
  28. public function copy()
  29. {
  30. return clone $this;
  31. }
  32. }

客户端代码

  1. public function prototypeImp()
  2. {
  3. $a = new Resume('大鸟');
  4. $a->setPersonalInfo('男', '29');
  5. $a->setWorkExperience('1998-2000', 'XX公司');
  6. //只需要调用Clone方法就可以实现新简历的生成,
  7. //并且可以再修改新简历的细节
  8. $b = $a->copy();
  9. $b->setWorkExperience('1998-2006', 'YY企业');
  10. $c = $a->copy();
  11. $c->setPersonalInfo('男', '24');
  12. $a->toShow();
  13. $b->toShow();
  14. $c->toShow();
  15. }

一般在初始化的信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,又对性能是大大的提高。
不用重新初始化对象,而是动态地获得对象运行时的状态。

9.5浅复制与深复制

当前克隆方法是这样,如果字段是值类型的,则对该字段执行逐位复制,如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。
结构图 第九章 简历复印——原型模式 - 图3代码实现:
工作经历类

  1. class WorkExperience
  2. {
  3. private $workDate = '';
  4. private $company = '';
  5. public function setWorkInfo(string $workDate, string $company)
  6. {
  7. $this->workDate = $workDate;
  8. $this->company = $company;
  9. }
  10. public function getWorkDate()
  11. {
  12. return $this->workDate;
  13. }
  14. public function getCompany()
  15. {
  16. return $this->company;
  17. }
  18. }

简历类

  1. class Resume extends ICloneable
  2. {
  3. private $name = '';
  4. private $sex = '';
  5. private $age = '';
  6. private $work = null;
  7. public function __construct(string $name)
  8. {
  9. $this->name = $name;
  10. $this->work = new WorkExperience();
  11. }
  12. public function setPersonalInfo(string $sex, string $age)
  13. {
  14. $this->sex = $sex;
  15. $this->age = $age;
  16. }
  17. public function setWorkExperience(string $timeArea, string $company)
  18. {
  19. $this->work->setWorkInfo($timeArea, $company);
  20. }
  21. public function toShow()
  22. {
  23. echo $this->name . ' ' . $this->sex . ' ' . $this->age . PHP_EOL;
  24. echo '工作经历:' . $this->work->getWorkDate() . ' ' . $this->work->getCompany() . PHP_EOL;
  25. }
  26. public function copy()
  27. {
  28. return clone $this;
  29. }
  30. }

客户端代码

  1. public function prototypeShallow()
  2. {
  3. $a = new ResumeShallow("大鸟");
  4. $a->setPersonalInfo('男', '29');
  5. $a->setWorkExperience('1998-2000', 'XX公司');
  6. $b = $a->copy();
  7. $b->setWorkExperience('1998-2006', 'YY企业');
  8. $c = $a->copy();
  9. $c->setPersonalInfo('男', '24');
  10. $c->setWorkExperience('1998-2003', 'ZZ企业');
  11. $a->toShow();
  12. $b->toShow();
  13. $c->toShow();
  14. }

输出结果

  1. //三次输出结果都是最后一次设置的值
  2. 大鸟 29
  3. 工作经历:1998-2003 ZZ企业
  4. 大鸟 29
  5. 工作经历:1998-2003 ZZ企业
  6. 大鸟 24
  7. 工作经历:1998-2003 ZZ企业

‘浅复制’,被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。但我们可能更需要这样的一种需求,把要复制的对象所引用的对象都复制一遍。比如刚才的例子,我们希望是a、b、c三个引用的对象都是不同的,复制时就一变二,二变三,此时,我们就叫这种方式为‘深复制’,深复制把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。
深复制要深入到多少层,需要事先就考虑好,而且要当心出现循环引用的问题,需要小心处理,这里比较复杂,可以慢慢研究。
Tips:待完善PHP的浅复制与深复制

9.6简历的深复制实现

结构图 第九章 简历复印——原型模式 - 图4代码实现
工作经历类

  1. class WorkExperience/* extends ICloneable*/
  2. {
  3. private $workDate = '';
  4. private $company = '';
  5. public function setWorkInfo(string $workDate, string $company)
  6. {
  7. $this->workDate = $workDate;
  8. $this->company = $company;
  9. }
  10. public function getWorkDate()
  11. {
  12. return $this->workDate;
  13. }
  14. public function getCompany()
  15. {
  16. return $this->company;
  17. }
  18. // public function copy()
  19. // {
  20. // return clone $this;/*unserialize(serialize($this));*/
  21. // }
  22. }

简历类

  1. class Resume extends ICloneable
  2. {
  3. private $name = '';
  4. private $sex = '';
  5. private $age = '';
  6. private $work = null;
  7. public function __construct(string $name)
  8. {
  9. $this->name = $name;
  10. $this->work = new WorkExperience();
  11. // $this->work = $work->copy();
  12. }
  13. public function setPersonalInfo(string $sex, string $age)
  14. {
  15. $this->sex = $sex;
  16. $this->age = $age;
  17. }
  18. public function setWorkExperience(string $timeArea, string $company)
  19. {
  20. $this->work->setWorkInfo($timeArea, $company);
  21. }
  22. public function toShow()
  23. {
  24. echo $this->name . ' ' . $this->sex . ' ' . $this->age . PHP_EOL;
  25. echo '工作经历:' . $this->work->getWorkDate() . ' ' . $this->work->getCompany() . PHP_EOL;
  26. }
  27. public function copy()
  28. {
  29. return unserialize(serialize($this));
  30. }
  31. }

客户端代码

  1. public function prototypeDeep()
  2. {
  3. $a = new ResumeDeep('大鸟');
  4. $a->setPersonalInfo('男', '31');
  5. $a->setWorkExperience('1998-2001', 'XX公司');
  6. $b = $a->copy();
  7. $b->setWorkExperience('1998-2006', 'YY企业');
  8. $c = $a->copy();
  9. $c->setPersonalInfo('男', '24');
  10. $c->setWorkExperience('1998-2003', 'ZZ企业');
  11. $a->toShow();
  12. $b->toShow();
  13. $c->toShow();
  14. }

输出结果

  1. 大鸟 31
  2. 工作经历:1998-2001 XX公司
  3. 大鸟 31
  4. 工作经历:1998-2006 YY企业
  5. 大鸟 24
  6. 工作经历:1998-2003 ZZ企业

在一些特定场合,会经常涉及深复制或浅复制,比如说,数据集对象DataSet,它就有Clone()方法和Copy()方法,Clone()方法用来复制DataSet的结构,但不复制DataSet的数据,实现了原型模式的浅复制。Copy()方法不但复制结构,也复制数据,其实就是实现了原型模式的深复制。
DataSet是ADO.NET的中心概念。可以把DataSet当成内存中的数据库,DataSet是不依赖于数据库的独立数据集合。
——百度百科