虽然抽象工厂把创建者与产品实现分离,但是也造成了每次添加产品家族时,都要创建一个相关的具体创建者,在一个快速增长的系统中,随着包含的产品越来越多,维护这种关系将会变得越来越复杂。为了避免这种情况,我们可以利用PHP中的clone关键字,生成产品对象自身的克隆,具体的产品类本身便成为了它们生成自己的基础。这样做不仅可以促进代码的灵活性,还能够减少创建的对象数目。使用组合代替了继承。
适用:
一、当要实例化的类是在运行时刻指定时。
二、为了避免创建一个与产品类层次平行的工厂类层次时。
三、当一个类的实例只能有几个不同状态组合中的一种时。
Prototype模式优点:
1、可以在运行时刻增加和删除产品
2、可以改变值或结构以指定新对象
3、减少子类的构造
4、用类动态配置应用
Prototype模式的缺点:
Prototype是的最主要的缺点就是每一个类必须包含一个克隆方法;
而且这个克隆方法需要对类的功能进行检测,这对于全新的类来说较容易,但对已有的类进行改造时将不是件容易的事情;
结构图:

Prototype:声明一个克隆自身的接口。
ConcretePrototype:实现一个克隆自身的操作。
Client:让一个原型克隆自身从而创建一个新的对象。
执行方式:客户请求原型克隆自身。
示例代码一:



在上面的程序中一共创建了两个对象,其中有一个对象是通过clone关键字克隆出来的副本。两个对象完全能独立,但他们中的成员及属性的值完全一样。如果需要对克隆后的副本对象在克隆时重新为成员属性赋初值,则可以在类中声明一个魔术方法“__clone()”。该方法是在对象克隆时自动调用的,所以就可以通过此方法对克隆后的副本重新初始化。__clone()方法不需要任何参数。将上例中的代码改写一下,在类中添加魔术方法__clone(),为副本对象中的成员属性重新初始化
序列化与反序列化实现深拷贝:
示例代码二:
//用原型实例指定创建对象的种类.并且通过拷贝这个原型来创建新的对象
适用:
一、当要实例化的类是在运行时刻指定时。
二、为了避免创建一个与产品类层次平行的工厂类层次时。
三、当一个类的实例只能有几个不同状态组合中的一种时。
Prototype模式优点:
1、可以在运行时刻增加和删除产品
2、可以改变值或结构以指定新对象
3、减少子类的构造
4、用类动态配置应用
Prototype模式的缺点:
Prototype是的最主要的缺点就是每一个类必须包含一个克隆方法;
而且这个克隆方法需要对类的功能进行检测,这对于全新的类来说较容易,但对已有的类进行改造时将不是件容易的事情;
结构图:

Prototype:声明一个克隆自身的接口。
ConcretePrototype:实现一个克隆自身的操作。
Client:让一个原型克隆自身从而创建一个新的对象。
执行方式:客户请求原型克隆自身。
示例代码一:
class Sea{
protected $navigability=0;
function __construct($navigability){
$this->navigability = $navigability;
}
}
class EarthSea extends Sea{}
class MarsSea extends Sea{}
class Plains{
protected $navigability=0;
function __construct($navigability){
$this->navigability = $navigability;
}
}
class EarthPlains extends Plains{}
class MarsPlains extends Plains{}
class Forest{
protected $navigability=0;
function __construct($navigability){
$this->navigability = $navigability;
}
}
class EarthForest extends Forest{
function getnavigability(){
return $this->navigability;
}
}
class MarsForest extends Forest{}
class TerrainFactory{
private $sea;
private $forest;
private $Palins;
function __construct(Sea $sea,Plains $plains,Forest $forest){
$this->sea = $sea;
$this->plains = $plains;
$this->forest = $forest;
}
function getSea(){
return clone $this->sea;
}
function getPlains(){
return clone $this->plains;
}
function getForest(){
return clone $this->forest;
}
}
$factory = new TerrainFactory(new EarthSea(-4),new EarthPlains(2),new EarthForest(3));
print_r($factory->getSea());
print_r($factory->getPlains());
print_r($factory->getForest()->getnavigability());
这个例子的结构图:
这里的clone方法是浅复制,即如果被克隆的对象引用了其他的对象,那么克隆出来的对象也是引用了以前的对象,这样当被克隆的对象改变了引用的对象时,克隆出来的对象所引用的对象也会被改变了。这种情况就是值对象模式出现的问题原理是相同的。我们可以使用拦截器__clone()方法实现深复制。
如果是浅复制的情况下改变值:class Contained {
public $now = 5;
}
class Container {
public $contained;
function __construct() {
$this->contained = new Contained();
}
// function __clone() {
// $this->contained = clone $this->contained;
// }
}
$original = new Container();
$copy = clone $original;
$original->contained->now = -1;//改变引用的值对象
print_r( $original );
print_r( $copy );
结果:
Result:Container Object ( [contained] => Contained Object ( [now] => -1 ) )
Container Object ( [contained] => Contained Object ( [now] => -1 ) )


在上面的程序中一共创建了两个对象,其中有一个对象是通过clone关键字克隆出来的副本。两个对象完全能独立,但他们中的成员及属性的值完全一样。如果需要对克隆后的副本对象在克隆时重新为成员属性赋初值,则可以在类中声明一个魔术方法“__clone()”。该方法是在对象克隆时自动调用的,所以就可以通过此方法对克隆后的副本重新初始化。__clone()方法不需要任何参数。将上例中的代码改写一下,在类中添加魔术方法__clone(),为副本对象中的成员属性重新初始化
class Contained { //被引用的对象
public $now = 5;
}
class Container {
public $contained;
function __construct() {
$this->contained = new Contained();
}
function __clone() {
$this->contained = clone $this->contained;
}
}
$original = new Container();
$copy = clone $original;
$original->contained->now = -1;
print_r( $original );
print_r( $copy );
结果:
Result:Container Object ( [contained] => Contained Object ( [now] => -1 ) ) Container Object ( [contained] => Contained Object ( [now] => 5 ) )
序列化与反序列化实现深拷贝:
示例代码二:
//用原型实例指定创建对象的种类.并且通过拷贝这个原型来创建新的对象
//声明一个克隆自身的接口,即抽象原型角色
interface Prototype{
public function copy();
}
//实现克隆自身的操作,具体原型角色
class ConcretePrototype implements Prototype{
private $name;
function __construct($name){
$this->name = $name;
}
function getName(){
return $this->name;
}
function setName($name){
$this->name = $name;
}
//克隆
function copy(){
//浅拷贝
//return clone $this;
//深拷贝
$serialize_obj = serialize($this); //序列化
$clone_obj = unserialize($serialize_obj); //反序列化
return $clone_obj;
}
}
//测试深拷贝的类
class Test{
public $array;
}
class Client{
//实现原型模式
public static function main(){
$test = new Test();
$test->array = array('1','2','3');
$pro1 = new ConcretePrototype($test);
print_r($pro1->getName());
$test->array = array('2','3','4');
$pro2 = $pro1->copy();
echo '<br />';
print_r($pro2->getName());
}
}
Client::main();
结果:
Test Object ( [array] => Array ( [0] => 1 [1] => 2 [2] => 3 ) )
Test Object ( [array] => Array ( [0] => 2 [1] => 3 [2] => 4 ) )