PHP设计模式-装饰模式实现非侵入式缓存

来源:互联网 发布:js函数not defined 编辑:程序博客网 时间:2024/06/14 00:08

本文的目标

  • 简单几步,轻松实现非侵入式缓存。

本文的涉及名词

  • 装饰模式 (也叫修饰模式,只是翻译不同)
  • 非侵入式
  • 依赖注入

名词解释

装饰模式

装饰模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。[修饰模式](https://zh.wikipedia.org/wiki/%E4%BF%AE%E9%A5%B0%E6%A8%A1%E5%BC%8F)

非侵入式

简单的说就是新加功能或者代码改原有代码

依赖注入

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。[控制反转](https://zh.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC)

具体实现

常见的缓存代码:

<?php class UserModel extends BaseModel{    public function getList($id)    {           $key = $this->getCacheKey($id);        $ret = Cache::get($key);        if (empty($ret)) {            $ret = $this->getFromDb($id);            Cache::put($key, $ret, 1000);        }        return $ret;    }    public function getCacheKey($id)    {        ......    }    public function geFromDb($id)    {        ......    }}

这样的代码带来的问题是缓存系统和数据代码高度耦合,如果需要更换或者撤销缓存系统,代码很难修改,也不好预测风险。接下来我们通过简单的几步来实现非侵入式缓存系统。

第一步先抽象出接口

<?php interface UserModelInterface {    public function getList($id);}

第二步UserModel实现 UserModelInterface

<?phpclass UserModel extends BaseModel implements UserModelInterface{    public function getList($id)    {        return $this->getFromDb($id);    }    ......}

第三步使用装饰模式建立UserModelCache

<?phpclass UserModelCache extends BaseModelCache implements UserModelInterface{    public function __construct(UserModel $model)    {        parent::__construct($model);    }    public function getList($id)    {        return $this->cache(__METHOD__, $id, 10000);    }    /**     * 需要缓存的数据都可以通过      * 这个类里来实现,可以通过 UserModelInterface 约束都需要实现的方法     * 如果不需要缓存的方法,会通过 BaseModelCache 的 __call 方法拦截,     * 并且穿透到 UserModel 的类里面去调用。     *      * 一个不需要cache的方法调用过程:     * 例如调用 UserModelCache->getAll()方法,因为 UserModelCache 没有该方法,     * 所以会寻找父类 BaseModelCache ,父类     * 也没有该方法,会被 BaseModelCache 的 __call 拦截,最后走到 UserModel->getAll()     * 这样,只要在UserModel 实现这个 getAll 就可以了。     *     * 需要cache的方法调用:     * 调用 getList 会走到父类的 cache 方法,cache 方法根据传递参数生成了 cahce id, 通过     * Cache 获取数据,获取不到数据,还会走 UserModel -> getList 方法,这样就实现了缓存功能,     * 而在 UserModel 里面没有嵌入任何代码。     *     * 关于如果灵活实用 cahce 的问题,稍后会介绍。     */}class BaseModelCache{       protected $model = null;    public function __construct(BaseModel $model)    {        $this->model = $model;    }    public function __call($method, $params)    {        // 通过 __call 方法来穿透 UserModelCache 不需要缓存或者未实现的方法,让 __call 去调用        // UserModel 的方法        return call_user_func_array([$this->model, $method], $params);    }    protected function cache($method, $params, $expire)    {        $key = $this->getCacheKey($method, $params);        $ret = Cache::get($key);        if (!$ret || CACHE_OPEN !== true) {            $ret = call_user_func_array([$this->model, $method], $params);            Cache::put($key, $ret, $expire);        }        return $ret;    }    protected function getCacheKey($method, $params)    {        return $key ....    }}

第四步灵活使用

<?php // 如果没有使用框架提供的依赖注入机制,那么就要自己手动完成了。// 例如调用 UserModel 的 getList 方法class UserController {    function index(UserModelInterface $user)    {        $user = new UserModelCache();        // 直接走 UserModel        // $user = new UserModel();        //         return $user->getList();    }}// 现在的很多框架都提供了依赖注入的功能,这些功能用起来更方便强大,可以让用户通过简单的配置// 来实现注入的内容,下面用 lumen 和 yii2 做两个简单的说明 

第五部 lumen 和 yii2 依赖注入

通过 lumen 的 ServiceProvider 来提供依赖注入

// // 具体的路径和名称空间根据自己的实际情况来// $app->when('App\Http\Controllers\UserController')->needs('App\Models\UserModelInterface')->give('App\Models\Cache\UserModelCache');//->give('App\Models\Cache\UserModel'); 不需要缓存// 在 controller 里面直接就被注入了 UserModelCache// 如果需要其他的缓存,只需要修改此处的 give 就行了,这样就实现了// 灵活配置,不需要对底层的代码进行修改// class UserController{    function index(UserModelInterface $user)    {        $user->getList();    }}

yii2 是通过 ServiceLoader 和 DI 容器来实现的,涉及 Container 相关的类,具体可以参考 yii2 的文档

$container = new yii\di\Container();$container->set('UserModelInterface', 'UserModelCache');// 使用$user = $container->get('UserModelInterface');

以上代都是为了演示,并不是真实使用环境,和真实环境有差异,请自行调整

原文发在 rust.love 上。

END.

0 0
原创粉丝点击