PHP 设计模式(杂项)

本文介绍PHP中常用的三个不属于GOF 的设计模式

  • 数据映射模式(Data Mapper Pattern )
  • 注 册 树 模 式(Registry Pattern)
  • 空 对 象 模 式(Null Object Pattern)

PHP设计模式(二十三)—数据映射模式(Data Mapper Pattern)

数据映射模式(Data Mapper Pattern ):描述如何创建提供透明访问任何数据源的对象。数据映射模式,也叫数据访问对象模式,或数据对象映射模式

(一)为什么需要数据映射模式

数据映射模式的目的是让持久化数据存储层、驻于内存的数据表现层、以及数据映射本身三者相互独立、互不依赖。这个数据访问层由一个或多个映射器(或者数据访问对象)组成,用于实现数据传输。通用的数据访问层可以处理不同的实体类型,而专用的则处理一个或几个。

(二)数据映射模式UML图

Data Mapper Pattern

(三)简单实例

通过数据对象映射模式,我们可以实现一个对象对应一条数据库记录,对象的属性对应记录的字段。但对象的属性改变时,自动更新数据库记录。

例如我们有一个用户类与数据库的用户表对应

<?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图

Null Object Pattern

上图是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行为型设计模式(四)

感谢阅读,由于笔者也是初学设计模式,能力有限,文章不可避免地有失偏颇
还请大家批评指正


我最近的学习总结:


欢迎大家关注我的微信公众号 火风鼎

转载于:https://juejin.im/post/59243549a0bb9f005f7658fd

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值