本文介绍PHP中常用的三个不属于GOF 的设计模式
- 数据映射模式(Data Mapper Pattern )
- 注 册 树 模 式(Registry Pattern)
- 空 对 象 模 式(Null Object Pattern)
PHP设计模式(二十三)—数据映射模式(Data Mapper Pattern)
数据映射模式(Data Mapper Pattern ):描述如何创建提供透明访问任何数据源的对象。数据映射模式,也叫数据访问对象模式,或数据对象映射模式。
(一)为什么需要数据映射模式
数据映射模式的目的是让持久化数据存储层、驻于内存的数据表现层、以及数据映射本身三者相互独立、互不依赖。这个数据访问层由一个或多个映射器(或者数据访问对象)组成,用于实现数据传输。通用的数据访问层可以处理不同的实体类型,而专用的则处理一个或几个。
(二)数据映射模式UML图
(三)简单实例
通过数据对象映射模式,我们可以实现一个对象对应一条数据库记录,对象的属性对应记录的字段。但对象的属性改变时,自动更新数据库记录。
例如我们有一个用户类与数据库的用户表对应
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图
暂无,跪求提供
(三)简单实例
注册树经常与单例模式一起使用,先查看注册树上是否有该实例,有就直接使用,没有就生成一个实例,并挂到树上。有些时候我们还可以这样做,让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
(三)简单实例
假设现在我们有这么一段代码
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
对象。而不是一个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()
什么都不做。或者你也可以让它抛出一个异常。
上一篇::
感谢阅读,由于笔者也是初学设计模式,能力有限,文章不可避免地有失偏颇
还请大家批评指正我最近的学习总结: