GoF类图

原型模式

代码实现

php原型模式PHP设计模式之原型模式 jQuery

abstractclassPrototype{public$v='clone'.PHP_EOL;publicfunction__construct(){echo'create'.PHP_EOL;}abstractpublicfunction__clone();}

首先我们通过仿照的办法定义了一个原型,这里紧张是仿照了__clone()这个方法。
实在这是PHP自带的一个魔术方法,根本是不须要我们去进行定义的,只须要在原型类中进行实现就可以了。
当外部利用clone关键字进行工具克隆时,直接就会进入这个魔术方法中。
在这个魔术方法里面我们可以对属性进行处理,特殊是针对引用属性进行一些独特的处理。
在这个例子中,我们只利用了一个值类型的变量。
无法表示出引用类型的问题,我们将在后面的实例中演示对引用类型变量的处理。

classConcretePrototype1extendsPrototype{publicfunction__clone(){}}classConcretePrototype2extendsPrototype{publicfunction__clone(){}}

仿照的详细实现的原型,实在便是紧张去详细的实现__clone()方法。
后面我们看详细的例子时再解释。

classClient{publicfunctionoperation(){$p1=newConcretePrototype1();$p2=clone$p1;echo$p1->v;echo$p2->v;}}$c=newClient();$c->operation();

客户端利用clone来复制\$p1,可以看到$p2也具有相同的$v属性。

原型模式看似便是复制了一个相同的工具,但是请把稳,复制的时候,__construct()方法并没有被调用,也便是当你运行这段代码的时候,create只输出了一次。
这也就带出了原型模式最大的一个特点——减少创建工具时的开销。
基于上述特点,我们可以快速的复制大量相同的工具,比如要给一个数组中塞入大量相同的工具时。
复制出来的工具中如果都是值类型的属性,我们可以任意修正,不会对原型产生影响。
而如果有引用类型的变量,则须要在__clone()方法进行一些处理,否则修正了复制工具的引用变量中的内容,会对原型工具中的内容有影响。

我们的手机操作系统(也可以想象一下PC电脑的操作系统),都是若何安装到设备中呢?实在都是一直的复制拷贝最初的那一套系统。
用微软的例子非常好解释这个问题,当年微软能够成为一个帝国,实在也是由于他一直的将winodws操作系统拷贝复制到光盘中,然后卖给千家万户(当然,这里没中国什么事儿)。
而中国市场呢,大量的高手破解了windows之后也是由这一份文件一直的复制拷贝才装到了我们的电脑中。
手机、智能设备等各种产品的操作系统、软件都是如此。
一次开拓无限拷贝正是软件行业暴利的缘故原由。
毕竟我们的系统也是由不少的工程师日以继夜的996在Android原生系统的根本上开拓出来的,赶紧不断的复制到即将出厂的手机上吧!

完全代码:https://github.com/zhangyue0503/designpatterns-php/blob/master/08.prototype/source/prototype.php

实例

同样还是拿手机来说事儿,这次我们是根据不同的运营商须要去开拓一批定制机,也便是套餐机。
这批手机说实话都并没有什么不同,大部分都是相同的配置,但是运营商系统不同,而且偶尔有一些型号的CPU和内存也可能存在不同。
这个时候,我们就可以用原型模式来进行快速的复制并且只修正一部分不相同的地方啦。

原型模式生产手机类图

原型模式生产手机

完全源码:https://github.com/zhangyue0503/designpatterns-php/blob/master/08.prototype/source/prototype-phone.php

<?phpinterfaceServiceProvicer{publicfunctiongetSystem();}classChinaMobileimplementsServiceProvicer{public$system;publicfunctiongetSystem(){return"中国移动".$this->system;}}classChinaUnicomimplementsServiceProvicer{public$system;publicfunctiongetSystem(){return"中国联通".$this->system;}}classPhone{public$service_province;public$cpu;public$rom;}classCMPhoneextendsPhone{function__clone(){//$this->service_province=newChinaMobile();}}classCUPhoneextendsPhone{function__clone(){$this->service_province=newChinaUnicom();}}$cmPhone=newCMPhone();$cmPhone->cpu="1.4G";$cmPhone->rom="64G";$cmPhone->service_province=newChinaMobile();$cmPhone->service_province->system='TD-CDMA';$cmPhone1=clone$cmPhone;$cmPhone1->service_province->system='TD-CDMA1';var_dump($cmPhone);var_dump($cmPhone1);echo$cmPhone->service_province->getSystem();echo$cmPhone1->service_province->getSystem();$cuPhone=newCUPhone();$cuPhone->cpu="1.4G";$cuPhone->rom="64G";$cuPhone->service_province=newChinaUnicom();$cuPhone->service_province->system='WCDMA';$cuPhone1=clone$cuPhone;$cuPhone1->rom="128G";$cuPhone1->service_province->system='WCDMA1';var_dump($cuPhone);var_dump($cuPhone1);echo$cuPhone->service_province->getSystem();echo$cuPhone1->service_province->getSystem();

解释

打印了很多东西呀,不过紧张的还是看看移动手机,也便是CMPhone中的__clone()方法,我们没有重新去初始化一个新工具。
这时,复制的$cmPhone1工具中的service_province和$cmPhone中的是同一个工具。
没错,这便是引用的复制问题。
引用只是复制了引用的地址,他们指向的是同一个工具。
当$cmPhone1修正service_province工具里面的属性内容时,$cmPhone里面的service_province工具里面的属性也随着改变了。
在CUPhone中,我们重新new了一个新的service_province工具。
这次表面的$cuPhone1对该工具中的属性修正时就不会影响$cuPhone中引用工具的值。
原型模式中最紧张的便是要把稳上述两点,而普通的值属性会直接进行复制,不会产生这个问题。
这里又牵扯出其余两个观点:浅复制和深复制浅复制,是指被复制工具的所有变量都含有与原来工具相同的值,而所有的对其他工具的引用都仍旧指向原来的工具深复制把引用工具的变量指向复制过的新工具,而不是原有的被引用的工具关于引用和值的问题,我们将在其他的文章中进行讲解,请关注微信或掘金号下期看点

原型模式虽然平常用得不多,但是学习之后创造还真是挺有用的,特殊是须要大量的重复工具时,可以大大节约新建工具的资源需求,往后还是须要多多练习早日运用在实际的业务场景中。
下一个又会是谁呢?别急别急,先去下个馆子,厨师、做事员、顾客,这三个要素就能组成一个神奇的模式:命令模式