php - 在 PHP 中为方法结果缓存实现装饰器模式的最佳方法

标签 php oop design-patterns caching decorator

我有一组类,它们习惯于使用相同的参数重复调用。这些方法通常运行数据库请求并构建对象数组等,因此为了消除这种重复,我构建了几个缓存方法来进行优化。这些是这样使用的:

应用缓存之前:

public function method($arg1, $arg2) {
$result = doWork();
return $result;
}

应用缓存后:

public function method($arg1, $arg2, $useCached=true) {
if ($useCached) {return $this->tryCache();}
$result = doWork();
return $this->cache($result);
}

不幸的是,我现在只剩下手动将它添加到所有方法的稍微费力的任务 - 我相信这是装饰器模式的一个用例,但我无法弄清楚如何以更简单的方式实现它在这种情况下使用 PHP。

最好的方法是什么,希望这些类中的任何所有方法自动执行此操作,或者我只需要在方法等中添加一行?

我看过覆盖返回语句等的方法,但看不到任何东西。

谢谢!

最佳答案

如果您不需要类型安全,您可以使用通用缓存装饰器:

class Cached
{
    public function __construct($instance, $cacheDir = null)
    {
        $this->instance = $instance;
        $this->cacheDir = $cacheDir === null ? sys_get_temp_dir() : $cacheDir;
    }

    public function defineCachingForMethod($method, $timeToLive) 
    {
        $this->methods[$method] = $timeToLive;
    }

    public function __call($method, $args)
    {
        if ($this->hasActiveCacheForMethod($method, $args)) {
            return $this->getCachedMethodCall($method, $args);
        } else {
            return $this->cacheAndReturnMethodCall($method, $args);
        }
    }

    // … followed by private methods implementing the caching

然后您可以像这样将需要缓存的实例包装到这个 Decorator 中:

$cachedInstance = new Cached(new Instance);
$cachedInstance->defineCachingForMethod('foo', 3600);

显然,$cachedInstance 没有foo() 方法。这里的技巧是utilize the magic __call method to intercept all calls to inaccessible or non-existing methods并将它们委托(delegate)给装饰实例。这样我们就可以通过装饰器公开装饰实例的整个公共(public) API。

如您所见,__call 方法还包含检查是否为该方法定义了缓存的代码。如果是这样,它将返回缓存的方法调用。如果没有,它将调用实例并缓存返回。

或者,您将专用的 CacheBackend 传递给装饰器,而不是在装饰器本身中实现缓存。然后装饰器将仅作为装饰实例和后端之间的中介。

这种通用方法的缺点是您的缓存装饰器将没有装饰实例的类型。当您的消费代码需要 Instance 类型的实例时,您将收到错误。


如果您需要类型安全的装饰器,您需要使用“经典”方法:

  1. 创建装饰实例公共(public) API 的接口(interface)。您可以手动执行此操作,或者如果工作量很大,请使用我的 Interface Distiller )
  2. 将每个需要装饰实例的方法的类型提示更改为接口(interface)
  3. 让 Decorated 实例实现它。
  4. 让 Decorator 实现它并将任何方法委托(delegate)给装饰实例
  5. 修改所有需要缓存的方法
  6. 对所有要使用装饰器的类重复此操作

简而言之

class CachedInstance implements InstanceInterface
{
    public function __construct($instance, $cachingBackend)
    {
        // assign to properties
    }

    public function foo()
    {
        // check cachingBackend whether we need to delegate call to $instance
    }
}

缺点是,它需要更多的工作。您需要为每个应该使用缓存的类执行此操作。您还需要将对缓存后端的检查放入每个函数(代码重复),并将不需要缓存的任何调用委托(delegate)给装饰实例(乏味且容易出错)。

关于php - 在 PHP 中为方法结果缓存实现装饰器模式的最佳方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17485854/

相关文章:

PHP递归函数问题

Javascript私有(private)变量和参数有什么区别?

algorithm - 在集合中寻找模式

java - 如果我们使用工厂模式,我们是否应该向用户隐藏产品?

php - 如何在 Zend 框架中设置表单样式?

javascript - 在类中显示 purejs 中的前 2 个字符并隐藏其余字符

php - 如何使用谷歌可视化API显示谷歌分析图

java - 构造函数是 OOP 的哪一部分?

java - 找不到在对象类中定义的符号(方法)

java - Java中接口(interface)调用的优化策略