本文介绍了在PHP中为方法结果缓存实现装饰器模式的最佳方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a set of classes which have a habit of being called repeatedly with the same arguments. These methods generally run database requests and build arrays of objects and such, and so to cut out this duplication I've constructed a couple of caching methods to optimise. These are used like so:

在应用缓存之前:

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中以更简单的方式实现它。

Unfortunately I'm now left with the slightly laborious task of manually adding this to all of the methods- I believe this is a use case of the decorator pattern but I can't figure out how to implement it in a simpler way in PHP for this case.

执行此操作的最佳方法是什么,希望这些类中的 all 方法自动执行此操作,还是只需要在方法中添加一行,等等?

What's the best way to do this, hopefully such that either all methods in any of these classes automatically do this, or I just have to add one line in the method etc?

我已经研究了重写return语句的方法,但实际上看不到任何东西。

I've had a look at ways to override the return statement and such but can't really see anything.

谢谢!

推荐答案

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

If you don't need Type Safety, you can use a generic Cache Decorator:

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中,如下所示:

You would then wrap an instance that needs caching into this Decorator like this:

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

很明显, $ cachedInstance 没有 foo()方法。这里的窍门是,并将它们委派给经过修饰的实例。这样,我们通过Decorator公开了装饰实例的整个公共API。

Obviously, the $cachedInstance does not have a foo() method. The trick here is to utilize the magic __call method to intercept all calls to inaccessible or non-existing methods and delegate them to the decorated instance. This way we are exposing the entire public API of the decorated instance through the Decorator.

如您所见, __ call 方法还包含用于检查是否为此定义了缓存的代码方法。如果是这样,它将返回缓存的方法调用。否则,它将调用实例并缓存返回值。

As you can see, the __call method also contains the code to check whether there is a caching defined for that method. If so, it will return the cached method call. If not, it will call the instance and cache the return.

或者,您将专用的CacheBackend传递给Decorator,而不是在装饰器本身中实现Caching。这样装饰器将仅作为装饰实例与后端之间的中介器。

Alternatively, you pass in a dedicated CacheBackend to the Decorator instead of implementing the Caching in the decorator itself. The Decorator would then only work as a Mediator between the decorated instance and the backend.

这种通用方法的缺点是您的缓存装饰器将不具有装饰实例的类型。当您的使用方代码期望实例类型为Instance时,您将得到错误。

The drawback of this generic approach is that your Cache Decorator will not have the type of the Decorated Instance. When your consuming code expects instances of type Instance, you will get errors.

如果您需要类型安全的修饰符,则需要使用经典方法:

If you need type-safe decorators, you need to use the "classic" approach:


  1. 创建装饰实例公共API的接口。您可以手动执行此操作,或者如果需要大量工作,请使用我的)

  2. 在每个需要装饰实例的接口上更改TypeHints

  3. 让装饰实例实现它。

  4. 让装饰器实现它并将任何方法委派给装饰的实例

  5. 修改所有需要缓存的方法

  6. 对要使用装饰器的所有类重复

  1. Create an Interface of the decorated instance public API. You can do that manually or, if it's a lot of work, use my Interface Distiller)
  2. Change the TypeHints on every method expecting the decorated instance to the Interface
  3. Have the Decorated instance implement it.
  4. Have the Decorator implement it and delegate any methods to the decorated instance
  5. Modify all methods that need caching
  6. Repeat for all classes that want to use the decorator

简而言之

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
    }
}

缺点是,它需要做更多的工作。您需要为应该使用缓存的每个类执行此操作。您还需要将对缓存后端的检查放到每个函数中(代码重复),并将不需要缓存的所有调用委派给经过修饰的实例(繁琐且容易出错)。

The drawback is, that it is more work. You need to do that for every class supposed to use caching. You'll also need to put the check to the cache backend into every function (code duplication) as well as delegating any calls that don't need caching to the decorated instance (tedious and error prone).

这篇关于在PHP中为方法结果缓存实现装饰器模式的最佳方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-13 13:17