Se non avete bisogno di sicurezza Tipo, è possibile utilizzare un generico decoratore cache:
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
Si potrebbe poi avvolgere un'istanza che ha bisogno di caching in questo Decorator in questo modo:
$cachedInstance = new Cached(new Instance);
$cachedInstance->defineCachingForMethod('foo', 3600);
Ovviamente, il $cachedInstance
non ha un metodo foo()
. Il trucco qui è utilize the magic __call
method to intercept all calls to inaccessible or non-existing methods e delegarli all'istanza decorata. In questo modo esponiamo l'intera API pubblica dell'istanza decorata tramite Decorator.
Come si può vedere, il metodo __call
contiene anche il codice per verificare se esiste una cache definita per tale metodo. In tal caso, restituirà la chiamata al metodo memorizzato nella cache. In caso contrario, chiamerà l'istanza e memorizzerà il reso nella cache.
In alternativa, si passa un CacheBackend dedicato al Decorator invece di implementare la memorizzazione nella cache nel decoratore stesso. Il decoratore avrebbe quindi funzionato solo come mediatore tra l'istanza decorata e il back-end.
L'inconveniente di questo approccio generico è che il Cache Decorator non avrà il tipo di Istanza decorata.Quando il tuo codice utente si aspetta istanze di tipo Istanza, otterrai degli errori.
Se avete bisogno di decoratori type-safe, è necessario utilizzare il metodo "classico":
- creare un'interfaccia dell'istanza decorato API pubblica. Potete farlo manualmente o, se si tratta di un sacco di lavoro, utilizzare il mio Interface Distiller)
- Modificare le TypeHints su ogni metodo in attesa l'istanza decorato all'interfaccia
- Hanno l'istanza Decorato attuarlo.
- Avere il decoratore applicarla e delegare tutti i metodi per l'istanza decorato
- Modificare tutti i metodi che hanno bisogno di caching
- Ripetere l'operazione per tutte le classi che vogliono utilizzare il decoratore
In poche parole
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
}
}
L'inconveniente è che è più lavoro. Devi farlo per ogni classe che si suppone utilizzi il caching. Dovrai anche inserire il controllo sul backend della cache in ogni funzione (duplicazione del codice) e delegare eventuali chiamate che non richiedono la memorizzazione nella cache dell'istanza decorata (noioso e soggetto a errori).
Violi Principio di sostituzione di Liskov, dare un'occhiata al progetto Proxy Manager, forse aiuta, mi ha aiutato – decebal
@ decebal L'approccio usando '__call' fa ovviamente, ma ho esplicitamente menzionato che non è sicuro, quindi è implicito. Il decoratore "classico" non viola LSP perché implementa la stessa interfaccia. – Gordon