内容简介:PHP 设计模式(杂项)
本文介绍 PHP 中常用的三个不属于 GOF 的设计模式
- 数据映射模式(Data Mapper Pattern )
- 注 册 树 模 式(Registry Pattern)
- 空 对 象 模 式(Null Object Pattern)
PHP设计模式(二十三)—数据映射模式(Data Mapper Pattern)
数据映射模式(Data Mapper Pattern ):描述如何创建提供透明访问任何数据源的对象。数据映射模式,也叫数据访问对象模式,或 数据对象映射模式 。
(一)为什么需要数据映射模式
数据映射模式的目的是让持久化数据存储层、驻于内存的数据表现层、以及数据映射本身三者相互独立、互不依赖。这个数据访问层由一个或多个映射器(或者数据访问对象)组成,用于实现数据传输。通用的数据访问层可以处理不同的实体类型,而专用的则处理一个或几个。
(二)数据映射模式UML图

(三)简单实例
通过数据对象映射模式,我们可以实现一个对象对应一条数据库记录,对象的属性对应记录的字段。但对象的属性改变时,自动更新数据库记录。
例如我们有一个用户类与数据库的用户表对应
<?php //数据模式映射类 class User { protected $id; protected $data; protected $db; protected $change = false; public function __construct($id) { $this->id = $id; //实例化数据库对象,这里使用了工厂方法 $this->db = Factory::getDatabase(); //从数据库查询数据,存放到data属性中 $this->data = $this->db->query("select * from user where id = $id limit 1"); } public function __get($key) { if (isset($this->data[$key])) { return $this->data[$key]; } } public function __set($key, $value) { $this->data[$key] = $value; $this->change = true; } //析构方法 public function __destruct() { //如果对象属性改变过,则change属性为true 则调更新方法更新数据库 $this->change && $this->update(); } //更新记录方法 public function update(){ foreach ($this->data as $k => $v) { $fields[] = "$k = '{$v}'"; } $this->db->query("update user set " . implode(', ', $fields) . "where id = {$this->id} limit 1"); } } //实例化对象 $user = new User(1); //改变名字 $user->name = 'admin';
如果我们要实现实时更新,也可以不要 change
属性,直接在 __set
方法中调用 update
方法,不用等到对象销毁前再统一更新。当然实时更新时更新方法可以精简地不需要 foreach
,只写更新一个字段指令就OK,但是这样也带来频繁操作数据库的问题。
PHP设计模式(二十四)—注册树模式(Registry Pattern)
注册树模式(Registry Pattern ):注册树模式为应用中经常使用的对象创建一个中央存储器来存放这些对象 —— 通常通过一个只包含静态方法的抽象类来实现(或者通过单例模式)。也叫做注册器模式
(一)为什么需要注册树模式
解决常用对象的存放问题,实现类似于全局变量的功能。
(二)注册树模式UML图
暂无,跪求提供
(三)简单实例
<?php //User类用于测试 class User{} //注册树类 class Registry { protected static $objects; //用于存放实例 //存入实例方法 static public function set($key, $object) { self::$objects[$key] = $object; } //获取实例方法 static public function get($key) { if (!isset(self::$objects[$key])) { return false; } return self::$objects[$key]; } //删除实例方法 static public function _unset($key) { unset(self::$objects[$key]); } } $user = new User; //存入实例 Registry::set('User',$user); //查看实例 var_dump(Registry::get('User')); //删除实例 Registry::_unset('User'); //再次查看实例 var_dump(Registry::get('User'));
注册树经常与单例模式一起使用,先查看注册树上是否有该实例,有就直接使用,没有就生成一个实例,并挂到树上。有些时候我们还可以这样做,让 get
方法如果get不到实例的时候就自动 new
一个存放起来,这样我们使用时就不用管有没有存放过这个实例,反正没有的话 get
方法也会帮我们存放。
//获取实例方法 static public function get($key) { if (!isset(self::$objects[$key])) { self::$objects[$key] = new $key; } return self::$objects[$key]; }
当然使用这种方式的话,查看实例是否存在,就不能使用 get
方法了。因为调用 get
方法以后,不存在也会生成一个实例。
PHP设计模式(二十五)—空对象模式(Null Object Pattern)
空对象模式(Null Object Pattern):用一个空对象取代 NULL,减少对实例的检查。这样的空对象可以在数据不可用的时候提供默认的行为
(一)为什么需要空对象模式
解决在需要一个对象时返回一个null值,使其调用函数出错的情况
(二)空对象模式UML图

上图是 Java 的空对象模式UML图,网上很多PHP设计模式的代码实现都是照着上面这个UML图
实际上PHP在空对象模式的实现上比Java更加简单些。因为PHP有美妙的语法糖,魔术方法 __call
方法。
我们只要实现空对象的 __call
方法就可以实现空对象模式,并不需要使用 nullobject
去继承对应的抽象 object
(三)简单实例
假设现在我们有这么一段代码
<?php //测试类 class Person{ public function code(){ echo 'code makes me happy'.PHP_EOL; } } //定义一个生成对象函数,只有PHPer才允许生成对象 function getPerson($name){ if($name=='PHPer'){ return new Person; } } $phper = getPerson('PHPer'); $phper->code();
是的,现在这段代码会输出 code makes me happy
。如果有时候这个函数是别人调用的,它并没传入合适的参数呢?
$phper = getPerson('Javaer'); $phper->code();
这个时候就会报错了 error : Call to a member function code() on null
。是的 $phper
现在是一个 null
值,所以调用 code
方法就会报错
这种情况很常见,系统并没有返回一个我们期待的对象,而是返回了一个 null值
。所以多数情况下,我们的代码都要这样写
$phper = getPerson('Javaer'); if(!is_null($phper)){ $phper->code(); }
或者是
if(is_object($phper)){ $phper->code(); }
这样让太多的 if判断
不可避免地存在于我们的代码中
如果我们使用NullObject模式的话,我们就可以让函数没有返回值时返回一个 nullobject
对象。而不是一个 null
值( 没有return 默认null值 )
//测试类 class Person{ public function code(){ echo 'code makes me happy'.PHP_EOL; } } //空对象模式 class NullObject{} //定义一个生成对象函数,只有PHPer才允许生成对象 function getPerson($name){ if($name=='PHPer'){ return new Person; }else{ return new NullObject; } } $phper = getPerson('PHer'); $phper->code();
这个时候就不会再报一个 null
调用函数的错误了,但是会报一个 call to undefined method的错误
,这是由于 NullObject
对象没有 code
方法。这个时候我们只需实现魔术方法 __call
,就不会报错了。
//空对象模式 class NullObject{ public function __call($method,$arg){ echo 'this is NullObject'; } }
我们可以通过返回一个 NullObject
对象来取代返回 null
,这样就不用在调用方法时判断是否为 null
,而且只要你实现了 __call
方法,不管真正的对象它原来是调用那个方法的, NullObject
都可以去调用而且不报错( 实际是隐式调用了魔术方法__call
)。当然,如果你原本的逻辑是返回对象是null的话什么都不做,那么你可以让 __call()
什么都不做。或者你也可以让它抛出一个异常。
上一篇:: PHP行为型设计模式(四)
感谢阅读,由于笔者也是初学设计模式,能力有限,文章不可避免地有失偏颇
还请大家批评指正
我最近的学习总结:
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- PHP 设计模式(杂项)
- 2018 DDCTF 杂项 writeup
- gobox中的异常定义和杂项工具
- gobox中的异常定义和杂项工具
- 搭建Typescript+React项目模板(3) --- 整理项目和杂项
- 设计模式-享元设计模式
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Agile Web Application Development with Yii 1.1 and PHP5
Jeffrey Winesett / Packt Publishing / 2010-08-27
In order to understand the framework in the context of a real-world application, we need to build something that will more closely resemble the types of applications web developers actually have to bu......一起来看看 《Agile Web Application Development with Yii 1.1 and PHP5》 这本书的介绍吧!