原型模式
意图
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
在创建型模式中,原型模式是相对简单的一个模式,我们主要从两点来理解原型模式:
- 原型模式如何实现的?
- 为什么要使用原型模式。
原型模式如何实现
当你想要复制一个对象时,你可以选择遍历该对象的成员并将其复制到新对象中,但是这样做你可能会获取不到一些私有成员,或者说你可能不清楚这个对象成员的相关依赖。所以,可以采用原型模式来实现复制对象的操作,原型模式将复制对象的任务委托给了对象本身。
为什么要使用原型模式
当你需要复制一些对象,但是你又希望复制的对象是单独的副本,你就可以使用原型模型。例如,当你收到第三方接口传递过来的对象时,在不了解对象信息也不想直接修改对象时,就可以使用原型模式来复制对象并进行操作。
此外,当你创建的实例差别很小时,那么你就可以使用原型模式来减少子类的数量。例如,页面模板可使用原型模式来复制生成。
实现
对于 PHP 而言,对复制对象的支持使得原型模式在 PHP 的使用变得非常简单。对于简单的类而言,直接使用 clone
关键字即可,但是如果类成员包括了引用类型,那就需要在 __clone
方法中实现对成员的复制。
<?php
class Component { }
class Prototype
{
public $component;
public $id;
public function __construct($component, $id)
{
$this->component = $component;
$this->id = $id;
}
public function __clone()
{
// 不希望 id 复制到新副本中
$this->id = 0;
// component 应当复制一个新的副本,否则将指向同一个 component 实例
$this->component = clone $this->component;
}
}
$object = new Prototype(new Component(), 1);
$clone = clone $object;
$clone->component === $object->component; // false;
$clone->id; // 0
应用
我们举一个常见的例子,将页面当成模板保存下来后反复使用,每次相当于复制一份副本。
实现
<?php
class Author
{
private $name;
private $pages = [];
public function __construct(string $name)
{
$this->name = $name;
}
public function addToPage(Page $page): void
{
$this->pages[] = $page;
}
}
class Page
{
private $title;
private $body;
private $author;
private $comments = [];
private $date;
public function __construct(string $title, string $body, Author $author)
{
$this->title = $title;
$this->body = $body;
$this->author = $author;
$this->author->addToPage($this);
$this->date = new \DateTime;
}
public function addComment(string $comment): void
{
$this->comments[] = $comment;
}
public function __clone()
{
$this->title = "标题副本" . $this->title;
$this->author->addToPage($this);
$this->comments = []; // 不需要拷贝评论
$this->date = new \DateTime;
}
}
测试
<?php
$author = new Author("心智极客");
$page = new Page("设计模式之原型模式", "内容", $author);
$page->addComment("这是一条评论");
$newPage = clone $page;
总结
PHP 对原型模式提供了良好的支持。通过原型模式,可以很方便的对对象进行克隆,而无须考虑具体的类。同时,通过原型模式,可以避免反复运行初始化代码。需要注意的是,如果对象非常复杂时,使用原型模式会变得非常的棘手。
推荐文章: