1.什么是原型模式?
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
2.原型模式解决的问题
用于创建对象成本过高时
“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是他们却拥有比较稳定一致的接口。
3.原型模式的应用场景
(1)类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗。
(2)通过new产生的一个对象需要非常繁琐的数据准备或者权限,这时可以使用原型模式。
(3)一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。
4.示例
<?php
/**
* 抽象原型角色
* 抽象原型
*/
abstract class PrototypeAbstract
{
/**
* 名称
* @var string
*/
protected $_name;
/**
* 打印对象名称
*
* @return sting
*/
abstract public function getName();
/**
* 获取原型对象
*
* @return object
*/
abstract public function getPrototype();
}
/**
* 具体原型角色
*原型实体
*/
class Prototype extends PrototypeAbstract
{
/**
* 构造函数
*
* @param string $name 属性一
* @return void
*/
public function __construct($name='')
{
$this->_name = $name;
}
/**
* 魔术方法 设置属性
*
* @param string $name 属性名称
* @param string $value 属性值
*/
public function __set($name='', $value='')
{
$this->$name = $value;
}
/**
* 打印对象名称
*
* @return sting
*/
public function getName()
{
echo '我是对象' . $this->_name . " \n\n";
}
/**
* 获取原型对象
*
* @return object
*/
public function getPrototype()
{
return clone $this;
}
}
class Client {
/**
* Main program.
*/
public static function main() {
// 创建一个原型对象
$prototype = new Prototype();
// 获取一个原型的clone
$prototypeCloneOne = $prototype->getPrototype();
$prototypeCloneOne->_name = 'one';
$prototypeCloneOne->getName();
// 获取一个原型的clone
$prototypeCloneTwo = $prototype->getPrototype();
$prototypeCloneTwo->_name = 'two';
$prototypeCloneTwo->getName();
// 再次获取$prototypeCloneOne的名称
$prototypeCloneOne->getName();
}
}
Client::main();
?>
5.原型模式优缺点
优点:
A、原型模式对客户隐藏了具体的产品类
B、运行时刻增加和删除产品: 原型模式允许只通过客户注册原型实例就可以将一个新的具体产品类并入系统。
C、改变值以指定新对象: 高度动态的系统允许通过对象复合定义新的行为。如通过为一个对象变量指定值并且不定义新的类。通过实例化已有类并且将实例注册为客户对象的原型,就可以有效定义新类别的对象。客户可以将职责代理给原型,从而表现出新的行为。
D、改变结构以指定新对象:许多应用由部件和子部件来创建对象。
E、减少子类的构造,Prototype模式克隆一个原型而不是请求一个工厂方法去产生一个新的对象。
F、用类动态配置应用 一些运行时刻环境允许动态将类装载到应用中。
G、使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
H、使用原型模式的另一个好处是简化对象的创建,使得创建对象很简单。
缺点:
原型模式的主要缺陷是每一个抽象原型Prototype的子类都必须实现clone操作,实现clone函数可能会很困难。当所考虑的类已经存在时就难以新增clone操作,当内部包括一些不支持拷贝或有循环引用的对象时,实现克隆可能也会很困难的。
6.原型模式分为浅复制和深复制
浅拷贝
被拷贝对象的所有变量都含有与原对象相同的值,而且对其他对象的引用仍然是指向原来的对象。即浅拷贝只负责当前对象实例,对引用的对象不做拷贝。
深拷贝
- 被拷贝对象的所有的变量都含有与原来对象相同的值,除了引用其他对象的变量。引用其他对象的变量将指向一个被拷贝的新对象,而不再是原有被引用对象。即深拷贝把要拷贝的对象所引用的对象也都拷贝了一次。
- 深拷贝要深入到多少层,是一个不确定的问题。在决定以深拷贝的方式拷贝一个对象的时候,必须决定对间接拷贝的对象是采取浅拷贝还是深拷贝还是继续采用深拷贝。因此,在采取深拷贝时,需要决定多深才算深。此外,在深拷贝的过程中,很可能会出现循环引用的问题。
PHP实现深拷贝
示例
/聚合类
class ObjA {
public $num = 0;
public $objB;//包含的对象
function __construct() {
$this->objB = new ObjB();
}
//只有实现了下面方法聚合类 才能实现深复制
/*function __clone() {
$this->objB = clone $this->objB;
}*/
}
class ObjB {
public $num2 = 0;
}