我有一组类,它们习惯于使用相同的参数重复调用。这些方法通常运行数据库请求并构建对象数组等,因此为了消除这种重复,我构建了几个缓存方法来进行优化。这些是这样使用的:
应用缓存之前:
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 类型的实例时,您将收到错误。
如果您需要类型安全的装饰器,您需要使用“经典”方法:
- 创建装饰实例公共(public) API 的接口(interface)。您可以手动执行此操作,或者如果工作量很大,请使用我的 Interface Distiller )
- 将每个需要装饰实例的方法的类型提示更改为接口(interface)
- 让 Decorated 实例实现它。
- 让 Decorator 实现它并将任何方法委托(delegate)给装饰实例
- 修改所有需要缓存的方法
- 对所有要使用装饰器的类重复此操作
简而言之
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/