设计模式专题(六)——原型模式
设计模式专题(六)
——原型模式
(原创内容,转载请注明来源,谢谢)
一、含义
原型模式(Prototype)是用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式实现从一个对象创建另外一个可以定制的对象,而不需要知道里面的细节。
因此,原型模式实现了克隆对象的功能,主要是把里面的非静态属性都克隆过来,如果是值类型就复制值,如果是引用、指向对象的,则另其指向一个新的对象,且其值和原值相同。这样,当要调用多次类的实例,且每次不一样,就不需要每次都去new,只需要克隆即可。
二、类图
三、实现方式
类的复制有深度复制与浅复制。
1、场景
现假设有两个类,Prototype类用于测试复制,Test类用于给属性赋值。$prototypeA实例化Prototype类,此时需要新增prototypeB实例。
classPrototype{
public $propStr;
public $propArr;
public $propObj;
public function__construct($str, $arr, $obj){
$this->propStr= $str;
$this->propArr= $arr;
$this->propObj= $obj;
}
}
class Test{
public $testStr;
public function__construct($str){
$this->testStr= $str;
}
}
$prototypeA = new Prototype(
'a',
array('a', array('aa','aaa')),
new Test('atest')
);
2、直接指向——$prototypeB=$prototypeA
此时,两个变量中的任意一个改动了其属性,另一个也会跟着改动。
3、浅复制——$prototypeB=clone$prototypeA
此时prototypeA中值类型的数据(数字、字符串、数组)可以完全复制过去到prototypeB,且prototypeB改动的情况下不会影响到prototypeA。但是,prototypeA的属性中如果有对象,则prototypeB如果对prototypeA的对象中的属性进行重新赋值,则prototypeA中相应的指向该对象的属性也一样会改动。
即,假设此时$prototypeB->propObj->testStr= 'btest'; 则A的propObj->testStr也是’btest’。
但是,如果不是直接操作属性指向的对象中的属性,则不会影响另一个变量。
如$prototypeB->propObj=newTest(‘btest’);或者等于其他任何的值,则A的propObj->testStr仍是’atest’。
4、深复制(1)——$prototypeB=unserialize(serialize($prototypeA))或json_decode(json_encode($prototypeA))
当将对象序列化或者转成json以后,即将对象完全转成字符串了,此时再还原字符串,则可以将B和A完全分离,即B是一个全新的A,两者无论如何改动自身都不影响到对方。
5、深复制(2)——魔术方法__clone()
当使用clone时,会自动调用类中的函数__clone(),如果没有定义则默认按照浅复制的方式对类进行复制。但是如果有定义的情况下,则可以根据需要进行复制。
例如:
public function __clone(){
//获取对象的所有属性值
$object_vars= get_object_vars($this);
foreach($object_vars as $attr_name => $attr_value){
//如果是对象,则用clone完全复制
if(is_object($this->$attr_name)){
$this->$attr_name= clone $this->$attr_name;
}elseif (is_array($this->$attr_name)){
//如果是数组,则逐个检查数组中的元素,如果元素中有对象则还需要clone
//这个只能完成一维数组的检查,如果是多维数组,需要用递归的方式检查
foreach($this->$attr_name as &$attr_array_value)
{
if(is_object($attr_array_value))
{
$attr_array_value= clone $attr_array_value;
}
unset($attr_array_value);
}
}
}
}
但此方式较为复杂,如果仅仅需要实现深度复制,还是用序列化或者json比较方便快捷。
通常__clone()魔术方法是为了控制复制的内容,当部分信息不希望被复制时才建立此方法,对具体的属性进行控制。
——written by linhxx 2017.07.31
相关阅读:
设计模式专题(五)——工厂方法模式
设计模式专题(四)——代理模式
设计模式专题(三)——装饰模式
设计模式专题(二)——策略模式
设计模式专题(一)——面向对象的设计原则
- 数据结构C#版笔记--双向链表(DbLinkList)
- 斐波那契数列与IE9
- DateTime.ToString()输出"年/月/日 时:分:秒"的格式
- Flash在线拍摄用户头象
- win7 64位下如何折腾Tubro C 3.0
- TweenLite的又一应用:图片的拼图加载效果
- mysql创建数据表时如何判断是否已经存在?
- 温故知新:接口的隐式实现与显式实现
- 也谈枚举ToString()性能的改进
- silverlight:利用telerik中的zip类对字符串进行压缩、解压
- 索引,视图,存储过程和触发器文档
- 重点解读:用小程序给公众号涨粉10w的7大行业案例
- 网络域名与注册商标冲突的解决途径
- 网站代码优化我们必须要做的那些事
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 设计原则之单一职责
- 设计原则之开闭原则
- SpringBoot执行跨域处理
- SpringBoot对全局异常的处理封装
- 自定义springboot-starter揭秘自动配置骚操作
- 【大厂面试题】Redis中是如何实现分布式锁的?
- 最近公司招人,研发组商量了下,暂时定下这么多java面试题!
- 市面上数据库种类那么多,如何选择?
- 玩转正则!推荐一个速查、调试、验证、可视化工具
- 当一个http请求来临时,SpringMVC究竟偷偷帮你做了什么?
- Js实现文本复制
- 当一个http请求来临时,SpringMVC究竟偷偷帮你做了什么?处理器映射器与处理器篇
- anetTcpGenericConnect 详解
- 详解 MySQL 基准测试和sysbench工具
- 第六天:网络处理(anet部分)-- redis源码慢慢学,慢慢看【redis6.0.6】