マジックキャッシングデコレータ

現在、私は、「正しくない」と書かれたプロジェクトの完成/書き直しに取り組んでいます。 途中で、作業を最適化するタスクがあります。 コードはもともと非常に非最適に書かれていました。 最適化作業の中で、キャッシュがねじ込まれています。



このプロジェクトにはいくつかの異なるデータソースがあり、その結果はキャッシュに適しています。主なものはもちろんデータベースです。 私は、最小限の血液で透明なソリューションを望んでいました。 ある時点では、フォームの構造を書くのにうんざりしていました



$query = "Select something"; $result = $cache->get($query, $tag); if (!$result) { $result = $db->queryAll($query); $cache->set($query, $tag); }
      
      





そして、私は何か他のものが欲しい。 もちろん、コードを別の関数またはメソッドに入れることもできますが、それはちょっとつまらないものです。さらに、異なる呼び出し(および$ db-> queryAllだけでなく、いくつかの異なるオプションがある)には、独自のコードと独自の関数が必要です/メソッド。



一方で、キャッシュコードをデータソースに直接追加することもあまり正しくありません。最終的には、これを行うべきではありません(そのため、 Traitsも適切でありません)。 別のキャッシュクラスを作成することもあまり便利ではありません。



一般に、さまざまなインターフェイスを備えたさまざまなデータソースに適した単一の汎用ソリューションが必要でしたが、同時に統一されていました。 「魔法の」デコレータを作成することにしました。



デコレータが何であるかを知らない場合、一般的には次のようになります。デコレータは、新しい動作をオブジェクトに動的に接続することを目的とするデザインパターンです。 したがって、この場合、データアクセスオブジェクトはシステムに対して同じままで、インターフェイスと動作はまったく同じですが、新しい(キャッシュ)動作がいくつかあります。



まさに私が望むもの:キャッシュされた*タイプの追加のメソッドがデータソースオブジェクトに現れるため。 たとえば、getData()メソッドがあり、それに加えて、getData()と同じインターフェースを持つcachedGetData()メソッドが表示されます。 デコレータは「マジック」メソッドを実行します。



だから、私たちは書く:



 class CachingDecorator { /** * @var object    . */ protected $obj; /** * @var object    . */ protected $cache; /** * @var string     - . */ protected $cacheTag; /** * @param type $object   * @param type $cache   * @param type $cacheTag    */ public function __construct($object, $cache, $cacheTag = 'query') { $this->obj = $object; $this->cache = $cache; $this->cacheTag = $cacheTag; } }
      
      





デコレータの初期化は次のようになります。



 $data = new CachingDecorator($data, $cache, 'remote');
      
      





しかし、これまでのところ、デコレータはデコレータではなく、装飾されたオブジェクトのようにはまったく動作しません。 これを修正するには、マジックを追加します(ゲッター/セッター、コール転送を追加):



  public function __get($name) { return $this->obj->$name; } public function __set($name, $value) { return $this->obj->$name = $value; } public function __call($name, $args) { return call_user_func_array(array($this->obj, $name), $args); }
      
      





さて、今オブジェクトの動作は自然と同じです(まあ、ほとんどですが、私たちの状況ではこれで十分です。何かが足りない場合は、必要な魔法のメソッドを追加します)。



通常、デコレータに簡単なメソッドが追加されます。 しかし、私たちは魔法が欲しいので、これを行います:



  public function __call($name, $args) { if (strtolower(substr($name, 0, 6)) == 'cached') { $name = substr($name, 6); $cacheName = md5(serialize($args)); $result = $this->cache->get($cacheName, $this->cacheTag); if ($result === false) { $result = call_user_func_array(array($this->obj, $name), $args); $this->cache->save($result, $cacheName, $this->cacheTag); } return $result; } else { return call_user_func_array(array($this->obj, $name), $args); } }
      
      





実際にはすべて。 必要なデータソースを装飾したら、代わりに書くことができます



 $result = $data->getDataById($id);
      
      





ただ:



 $result = $data->cachedGetDataById($id);
      
      





これはとても簡単で、キャッシュを操作するためにこれ以上庭は必要ありません。



更新:ここでは、柔軟性が失われると書いた個人的な電子メールで、キャッシュの有効期間を指定する方法はありません。 私の場合、これは単に関連性がなく、キャッシュオブジェクトの初期化時に指定された時間が使用されます。 ただし、必要な場合は、単にデコレータを拡張できます。 たとえば、最初のパラメーターにキャッシュの有効期間を追加することにより、キャッシュされた*関数のインターフェイスを変更できます。 または、異なるキャッシュライフタイムを使用するマジックメソッドを追加します。たとえば、fastCached *およびslowCached *(頻繁に更新されるデータとまれに更新されるデータ用)です。



All Articles