原型模式其实更形象的来说应该叫克隆模式。它主要的行为是对对象进行克隆,但是又把被克隆的对象称之为最初的原型,于是,这个模式就这样被命名了。说真的,从使用方式来看真的感觉叫克隆模式更贴切一些。

Gof类图及解释

GoF定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象

GoF类图

代码实现

abstract class Prototype
{
public $v = 'clone' . PHP_EOL; public function __construct()
{
echo 'create' . PHP_EOL;
} abstract public function __clone();
}

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

class ConcretePrototype1 extends Prototype
{
public function __clone()
{
}
} class ConcretePrototype2 extends Prototype
{
public function __clone()
{
}
}

模拟的具体实现的原型,其实就是主要去具体的实现__clone()方法。后面我们看具体的例子时再说明。

class Client
{
public function operation()
{
$p1 = new ConcretePrototype1();
$p2 = clone $p1; echo $p1->v;
echo $p2->v;
}
} $c = new Client();
$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

<?php
interface ServiceProvicer
{
public function getSystem();
} class ChinaMobile implements ServiceProvicer
{
public $system;
public function getSystem(){
return "中国移动" . $this->system;
}
}
class ChinaUnicom implements ServiceProvicer
{
public $system;
public function getSystem(){
return "中国联通" . $this->system;
}
} class Phone
{
public $service_province;
public $cpu;
public $rom;
} class CMPhone extends Phone
{
function __clone()
{
// $this->service_province = new ChinaMobile();
}
} class CUPhone extends Phone
{
function __clone()
{
$this->service_province = new ChinaUnicom();
}
} $cmPhone = new CMPhone();
$cmPhone->cpu = "1.4G";
$cmPhone->rom = "64G";
$cmPhone->service_province = new ChinaMobile();
$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 = new CUPhone();
$cuPhone->cpu = "1.4G";
$cuPhone->rom = "64G";
$cuPhone->service_province = new ChinaUnicom();
$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中引用对象的值。
  • 原型模式中最主要的就是要注意上述两点,而普通的值属性会直接进行复制,不会产生这个问题。这里又牵涉出另外两个概念:浅复制深复制
  • 浅复制,是指被复制对象的所有变量都含有与原来对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象
  • 深复制把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象
  • 关于引用和值的问题,我们将在其他的文章中进行讲解,请关注微信或掘金号

下期看点

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

关注公众号:【硬核项目经理】获取最新文章

添加微信/QQ好友:【xiaoyuezigonggong/149844827】免费得PHP、项目管理学习资料

知乎、公众号、抖音、头条搜索【硬核项目经理】

B站ID:482780532

PHP设计模式之原型模式的更多相关文章

  1. 设计模式_11_原型模式(prototype)深拷贝、浅拷贝

    设计模式_11_原型模式(prototype) 浅拷贝: package designPatternOf23; /** * 定义:用原型实例,指定创建对象的种类,并通过拷贝这些原型创建新的对象 * P ...

  2. C#设计模式(6)——原型模式(Prototype Pattern)

    一.引言 在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创建这样的类实例,这未免会增加创建类的复杂度和耗费更多的内存空间,因为这样在 ...

  3. 乐在其中设计模式(C#) - 原型模式(Prototype Pattern)

    原文:乐在其中设计模式(C#) - 原型模式(Prototype Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 原型模式(Prototype Pattern) 作者:weba ...

  4. C#设计模式之六原型模式(Prototype)【创建型】

    一.引言 在开始今天的文章之前先说明一点,欢迎大家来指正.很多人说原型设计模式会节省机器内存,他们说是拷贝出来的对象,这些对象其实都是原型的复制,不会使用内存.我认为这是不对的,因为拷贝出来的每一个对 ...

  5. C#设计模式之五原型模式(Prototype Pattern)【创建型】

    一.引言 在开始今天的文章之前先说明一点,欢迎大家来指正.很多人说原型设计模式会节省机器内存,他们说是拷贝出来的对象,这些对象其实都是原型的复制,不会使用内存.我认为这是不对的,因为拷贝出来的每一个对 ...

  6. C#设计模式(6)——原型模式(Prototype Pattern)(转)

    一.引言 在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创建这样的类实例,这未免会增加创建类的复杂度和耗费更多的内存空间,因为这样在 ...

  7. C#设计模式(6)——原型模式(Prototype Pattern) C# 深浅复制 MemberwiseClone

    C#设计模式(6)——原型模式(Prototype Pattern)   一.引言 在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创 ...

  8. JAVA 设计模式之原型模式

    目录 JAVA 设计模式之原型模式 简介 Java实现 1.浅拷贝 2.深拷贝 优缺点说明 1.优点 2.缺点 JAVA 设计模式之原型模式 简介 原型模式是六种创建型设计模式之一,主要应用于创建相同 ...

  9. C# Json反序列化 C# 实现表单的自动化测试<通过程序控制一个网页> 验证码处理类:UnCodebase.cs + BauDuAi 读取验证码的值(并非好的解决方案) 大话设计模式:原型模式 C# 深浅复制 MemberwiseClone

    C# Json反序列化   Json反序列化有两种方式[本人],一种是生成实体的,方便处理大量数据,复杂度稍高,一种是用匿名类写,方便读取数据,较为简单. 使用了Newtonsoft.Json,可以自 ...

  10. GOF23设计模式之原型模式

    GOF23设计模式之原型模式 1)通过 new 产生一个对象需要飞船繁琐的数据准备或访问权限,则可以使用原型模式. 2)就算 java 中的克隆技术,以某个对象为原型,复制出新的对象.显然,新的对象具 ...

随机推荐

  1. Create Shortcut to Get Jar File Meta Information

    You have to get meta information of cobertura.jar with command "unzip -q -c cobertura.jar META- ...

  2. 移动端 CPU 的深度学习模型推理性能优化——NCHW44 和 Record 原理方法详解

    用户实践系列,将收录 MegEngine 用户在框架实践过程中的心得体会文章,希望能够帮助有同样使用场景的小伙伴,更好地了解和使用 MegEngine ~ 作者:王雷 | 旷视科技 研发工程师 背景 ...

  3. 自己动手实现Lua--实现TAILCALL指令

    最近在看<自己动手实现Lua-虚拟机.编译器和标准库>.这是本挺不错的书,通过学习此书能够对Lua语言有比较深刻的理解,此外还可以对如何自己实现一门脚本语言有直观的认识.对于想学习Lua的 ...

  4. [数据库系列之MySQL] Mysql整体架构浅析一

    一.引言 平时我们在做Java系统时,一般情况下都会连接到一个MySQL数据库上去,执行各种增删改查的语句.大部分的Java工程师对MySQL的了解和掌握程度,大致就停留在这么一个阶段:对MySQL可 ...

  5. SQL 练习23

    查询男生.女生人数 SELECT Ssex,COUNT(Ssex) 人数 from Student GROUP BY Ssex

  6. 备战秋招之十大排序——O(nlogn)级排序算法

    时间复杂度O(nlogn)级排序算法 五.希尔排序 首批将时间复杂度降到 O(n^2) 以下的算法之一.虽然原始的希尔排序最坏时间复杂度仍然是O(n^2),但经过优化的希尔排序可以达到 O(n^{1. ...

  7. Docker开启TLS和CA认证

    前言:Docker直接开启2375端口是不安全的,别人只要连上之后就可以任意操作,下面是开启Docker的TLS和CA认证方法,并使用Jenkins和Portainer连接. 一.生成证书 查看服务器 ...

  8. python创建一个简单的服务

    python -m http.server 8000 --bind 0.0.0.0 8000为端口 0.0.0.0允许远程访问

  9. maven打包war,导入本地jar包

    方法1: 一 . 在项目根目录创建lib文件夹,把jar放入lib文件夹中 二 . 在项目中使用本地jar pom文件配置如下: <properties> <project.buil ...

  10. Spring-boot注入配置为java静态属性

    配置文件 注入静态变量 注意 类上面有:@Component注解 使用注入的静态变量 成功