文章大纲

细致入微地讲解php设计模式:原型模式

2020-03-26 00:49:58
先抛开程序观念,通过一个简单的例子,来说明一下原型设计模式的好处:
我们慢慢用键盘打字,写了一封邮件。发出去后,发现有些人漏发了,要再发一次。接下来,你会直接复制刚才发的邮件内容,而不是从头开始重新写邮件,复制过来的邮件内容,你发觉里面的称呼,不适合当前的收件人,你还可以去修改邮件内容,但这并不会影响你刚才已经发出去的邮件。其中复制内容、可修改就是本文要讲的原型设计模式的好处。

原型设计模式的好处:
创建快,可修改

废话不多说,下面直接结合程序代码,再来理解一下这个原型设计模式。
<?php

//轮胎类
class Wheel{
    //轮胎半径
    private $radius = 10;

    public function setRadius($value){
        $this->radius = $value;
    }
}

//车门类
class Door{

}


//小汽车类
class Car{

    private $wheel;
    private $door;

    //车内温度
    private $temperature;

    public function __construct(Wheel $wheel, Door $door)
    {
        $this->wheel = $wheel;
        $this->door  = $door;
        echo "--- new a car construct ----<br>";
        $this->temperature = 30;
    }

    public function setTemperature($newValue){
        $this->temperature = $newValue;
    } 

}

$wheel = new Wheel();
$door  = new Door();
$car1 = new Car($wheel, $door);
$car2 = clone $car1;  //拷贝原型对象
$car2->setTemperature(25);
var_dump($car1);
var_dump($car2);

上面程序在浏览器打印结果如下:
--- new a car construct ----
F:\wamp\www\design\prototype\clone.php:49:
object(Car)[3]
  private 'wheel' => 
    object(Wheel)[1]
      private 'radius' => int 10
  private 'door' => 
    object(Door)[2]
  private 'temperature' => int 30
F:\wamp\www\design\prototype\clone.php:50:
object(Car)[4]
  private 'wheel' => 
    object(Wheel)[1]
      private 'radius' => int 10
  private 'door' => 
    object(Door)[2]
  private 'temperature' => int 25

原型设计模式就是通过拷贝(clone)原型对象来新建对象,而不是通过new关键字来创建对象。

有什么好处?

从上面打印结果就可以看出来:
1. clone对象,不用执行原型对象的构造函数。对应“创建快”的优点
解析:上面输出的new a car construct 内容,是new Car类时执行构造函数时输出的。构造函数里面的工作,就类似上面例子中的打字写信的过程。clone对象,就不会执行原型类Car的构造函数,就相当于省掉了重新打字写信的过程。

2.新对象属性可修改,不会影响其它同类对象。对应“可修改”的优点
解析:上面输出就可以看出,car2对象车内温度设置为25摄氏度后,car1对象车内温度仍然是30度,并不会跟着变化。

从“可修改”的优点出发,拷贝还可以分为“深拷贝”和“浅拷贝”。上面的代码实现的是“浅拷贝”,实际应用大多都是“深拷贝”。

那么什么是深拷贝和浅拷贝呢?

先继续利用上面程序,来说说浅拷贝是怎么回事儿?

在上面程序末尾添加如下两行代码:
$wheel->setRadius(20);
var_dump($car2);

打印结果如下:
  F:\wamp\www\design\prototype\clone.php:52:
object(Car)[4]
  private 'wheel' => 
    object(Wheel)[1]
      private 'radius' => int 20
  private 'door' => 
    object(Door)[2]
  private 'temperature' => int 25

你关注下车轮半径值,由10变为20了,有什么问题么?
......
问题就在于:
我修改的wheel类是用于创建car1对象的,却影响到了car2对象。通俗点说,我在改一辆车的轮胎大小,另一辆车的轮胎大小也跟着同样变化了,这就是浅拷贝所带来的问题。

翟码农自己给浅拷贝下一个定义:
浅拷贝:针对原对象的属性和方法,所有拷贝类拥有独立的一份。但对于依赖的类对象,所有拷贝类依赖的类对象都是同一个(即依赖的类对象在内存里都是共同的区域)。

深拷贝就是解决浅拷贝的问题而产生的概念。

php里深拷贝如何实现呢?

在上面程序里Car类里添加如下方法:
public function __clone()
{
	echo "--- enter clone function ---<br>";
	$this->wheel = clone $this->wheel;
	$this->door  = clone $this->door;
}

__clone是魔术方法,在使用clone关键字时,会自动调用原型类的这个方法。
再看上面程序里car2对象的内容,就会发现车轮半径是10了。在末尾再次打印car1对象内容,发现车轮半径改成20了。

这就说明,此时car1对象和car2对象依赖的车轮类对象都是独立的了。这就是我们所说的深拷贝。

原型设计模式的应用场景:

通篇下来,就可以理解,当需要大量创建某种类的实例时,就可以采用此模式,避免执行构造函数,减少内存消耗,节省时间,即加快了实例的生成。

本文为翟码农个人博客里有关原型设计模式的原创文章,转载请注明出处:http://www.zhai14.com/blog/design-pattern-of-prototype.html


我要评论
评论列表