ホームページ >バックエンド開発 >PHPチュートリアル >サブタイプの多型 - 実行時に実装を交換します
コアポイント
コンポーネントのインターフェイスと実装を定義します
拡張可能なキャッシュコンポーネントを構築するときに選択できるオプションのメニューは決してありません(これについて懐疑的な場合は、いくつかの一般的なフレームワークの背後にあるものを見てください)。ただし、ここでは、私が提供するコンポーネントには、クライアントコードを変更せずに、実行時に異なるキャッシュドライバーを交換する巧妙な能力があります。それで、開発プロセス中に多くの努力なしでこれをどのように行うことができますか?まあ、最初のステップは...はい、後で異なる実装が続く孤立したキャッシュ契約を定義し、それによって多型の利点を利用します。最も基本的なレベルでは、上記の契約は次のとおりです。
<code class="language-php"><?php namespace LibraryCache; interface CacheInterface { public function set($id, $data); public function get($id); public function delete($id); public function exists($id); }</code>
インターフェイスは、一般的なキャッシュ要素の動作を抽象化するスケルトン契約です。インターフェイスを使用すると、契約に準拠する特定のキャッシュ実装を簡単に作成できます。私はそれをシンプルで理解しやすいものにしたいので、私が設定したキャッシュドライバーは単なる無駄のないデュオになります:最初のものはファイルシステムをキャッシュの基礎となるバックエンドとして使用します/データを取得します。舞台裏。以下は、ファイルベースのキャッシュの実装です
CacheInterface
クラスの運転ロジックは理解しやすいはずです。これまでのところ最も関連性の高いことは、初期の
<code class="language-php"><?php namespace LibraryCache; class FileCache implements CacheInterface { const DEFAULT_CACHE_DIRECTORY = 'Cache/'; private $cacheDir; public function __construct($cacheDir = self::DEFAULT_CACHE_DIRECTORY) { $this->setCacheDir($cacheDir); } public function setCacheDir($cacheDir) { if (!is_dir($cacheDir)) { if (!mkdir($cacheDir, 0644)) { throw InvalidArgumentException('The cache directory is invalid.'); } } $this->cacheDir = $cacheDir; return $this; } public function set($id, $data) { if (!file_put_contents($this->cacheDir . $id, serialize($data), LOCK_EX)) { throw new RuntimeException("Unable to cache the data with ID '$id'."); } return $this; } public function get($id) { if (!$data = unserialize(@file_get_contents($this->cacheDir . $id, false))) { throw new RuntimeException("Unable to get the data with ID '$id'."); } return $data; } public function delete($id) { if (!@unlink($this->cacheDir . $id)) { throw new RuntimeException("Unable to delete the data with ID '$id'."); } return $this; } public function exists($id) { return file_exists($this->cacheDir . $id); } }</code>の別の合理化された実装を実現しましょう。次の実装はインターフェイス契約に準拠していますが、今回はAPCを使用してバンドル方法を拡張することです。
FileCache
CacheInterface
クラスは、キャリアで見た中で最も見事なAPCラッパーではなく、メモリからデータを保存、取得、削除するために必要なすべての機能を梱包します。私たちは、特定のバックエンドがポリモーフィズムのために実行時に簡単に交換できるだけでなく、将来バックエンドを追加することも非常に簡単な軽量キャッシュモジュールを正常に実装したため、自分自身を称賛しましょう。 CacheInterface
に準拠した別の実装を書くだけです。ただし、実際のサブタイプの多型は、非常に一般的なアプローチであるインターフェイス構造を通じて定義された契約を実装することで達成されることを強調する必要があります。ただし、抽象的なメソッドのセットとして宣言されたインターフェイスを切り替えることで、正統派の方が少ないことを妨げ、同じ結果を得ることができません。危険を感じ、そのバイパスに行きたい場合は、次のように契約と対応する実装を再構築できます。
<code class="language-php"><?php namespace LibraryCache; interface CacheInterface { public function set($id, $data); public function get($id); public function delete($id); public function exists($id); }</code>
<code class="language-php"><?php namespace LibraryCache; class FileCache implements CacheInterface { const DEFAULT_CACHE_DIRECTORY = 'Cache/'; private $cacheDir; public function __construct($cacheDir = self::DEFAULT_CACHE_DIRECTORY) { $this->setCacheDir($cacheDir); } public function setCacheDir($cacheDir) { if (!is_dir($cacheDir)) { if (!mkdir($cacheDir, 0644)) { throw InvalidArgumentException('The cache directory is invalid.'); } } $this->cacheDir = $cacheDir; return $this; } public function set($id, $data) { if (!file_put_contents($this->cacheDir . $id, serialize($data), LOCK_EX)) { throw new RuntimeException("Unable to cache the data with ID '$id'."); } return $this; } public function get($id) { if (!$data = unserialize(@file_get_contents($this->cacheDir . $id, false))) { throw new RuntimeException("Unable to get the data with ID '$id'."); } return $data; } public function delete($id) { if (!@unlink($this->cacheDir . $id)) { throw new RuntimeException("Unable to delete the data with ID '$id'."); } return $this; } public function exists($id) { return file_exists($this->cacheDir . $id); } }</code>
<code class="language-php"><?php namespace LibraryCache; class ApcCache implements CacheInterface { public function set($id, $data, $lifeTime = 0) { if (!apc_store($id, $data, (int) $lifeTime)) { throw new RuntimeException("Unable to cache the data with ID '$id'."); } } public function get($id) { if (!$data = apc_fetch($id)) { throw new RuntimeException("Unable to get the data with ID '$id'."); } return $data; } public function delete($id) { if (!apc_delete($id)) { throw new RuntimeException("Unable to delete the data with ID '$id'."); } } public function exists($id) { return apc_exists($id); } }</code>
上から下へ、これは実際にはポリ型アプローチであり、以前に説明した方法に反しています。個人的には、これは私の個人的な声明であり、インターフェイスコンストラクトを使用して契約を定義し、いくつかのサブタイプで共有されたボイラープレートの実装をカプセル化する場合にのみ抽象クラスを使用することを好みます。ニーズに最適な方法を選択できます。この時点で、私はカーテンを置き、いくつかの派手なエンディングコメントを書いたり、印象的なコーディングスキルについて自慢したり、キャッシュコンポーネントの柔軟性について自慢したりすることができましたが、それは私たちにとって前かがみになります。複数の実装を使用できるクライアントコードがある場合、これらの実装が予想される契約を満たしている限り、これらの実装が何らかのタイプのインスタンスであるかどうかを確認せずに、多型が最も魅力的な側面を示します。それでは、キャッシュコンポーネントを基本的なクライアントビュークラスに接続することで側面を明らかにしましょう。これにより、きちんとしたHTMLキャッシュを簡単に実行できます。
キャッシュドライバーを使用する
キャッシュモジュールを介したキャッシュHTML出力は非常に簡単であり、他の時間に長い説明を保存します。キャッシュプロセス全体を、以下と同様に、単純なビュークラスに簡素化できます。
<code class="language-php"><?php namespace LibraryCache; abstract class AbstractCache { abstract public function set($id, $data); abstract public function get($id); abstract public function delete($id); abstract public function exists($id); }</code>最も見事な男はクラスコンストラクターであり、
<code class="language-php"><?php namespace LibraryCache; class FileCache extends AbstractCache { // the same implementation goes here }</code>メソッドの初期の実装者と
メソッドを使用しています。最後の方法の責任は、ビューのテンプレートが出力バッファーにプッシュされた後にキャッシュすることであるため、この機能を利用してHTMLドキュメント全体をキャッシュすることは良いことです。ビューのデフォルトテンプレートには次の構造があると仮定します。
CacheInterface
render()
さあ、ビューに
<code class="language-php"><?php namespace LibraryCache; class ApcCache extends AbstractCache { // the same implementation goes here }</code>
とても良いですよね?でも待って!私はとても興奮していたので、上記のコードスニペットがAPC拡張機能をインストールしていないシステムで爆発することを忘れていました(Naughty System Administrator!)。これは、慎重に作成されたキャッシュモジュールが再利用できなくなったことを意味しますか?これはまさにファイルベースのドライバーが出てくる場所です。これは、苦情を受け取らずにクライアントコードに入れることができます:ApcCache
<code class="language-php"><?php namespace LibraryView; interface ViewInterface { public function setTemplate($template); public function __set($field, $value); public function __get($field); public function render(); }</code>上記の単一行のコードは、ビューが共有メモリの代わりにファイルシステムを使用して出力をキャッシュすることを明示的に述べています。この動的スイッチングキャッシュバックエンドは、高度に分離されたモジュールを設計する際に多型が非常に重要である理由を簡単に示しています。これにより、脆弱性/剛性関連のアーティファクトをシステムの他の部分に広めることなく、実行時に物事を簡単に再接続できます。
<code class="language-php"><?php namespace LibraryView; use LibraryCacheCacheInterface; class View implements ViewInterface { const DEFAULT_TEMPLATE = 'default'; private $template; private $fields = array(); private $cache; public function __construct(CacheInterface $cache, $template = self::DEFAULT_TEMPLATE) { $this->cache = $cache; $this->setTemplate($template); } public function setTemplate($template) { $template = $template . '.php'; if (!is_file($template) || !is_readable($template)) { throw new InvalidArgumentException( "The template '$template' is invalid."); } $this->template = $template; return $this; } public function __set($name, $value) { $this->fields[$name] = $value; return $this; } public function __get($name) { if (!isset($this->fields[$name])) { throw new InvalidArgumentException( "Unable to get the field '$field'."); } return $this->fields[$name]; } public function render() { try { if (!$this->cache->exists($this->template)) { extract($this->fields); ob_start(); include $this->template; $this->cache->set($this->template, ob_get_clean()); } return $this->cache->get($this->template); } catch (RuntimeException $e) { throw new Exception($e->getMessage()); } } }</code>結論
多型は実際に人生の良いことの1つであり、一度それを理解すると、そのケースが長く続くことなくどうすればいいのかと思うようになります。多型システムは、本質的により直交的で、縮尺が容易であり、オープン/クローズド原理や賢明な「インターフェイス指向プログラミング」の原則などのコアパラダイムに違反する傾向がありません。むしろ原始的ですが、キャッシュモジュールはこれらの利点の顕著な例です。多型の利点を活用するためにアプリケーションをリファクタリングしていない場合は、ジャックポットを逃したので急いでください! Fotoliaの写真
に関するFAQ サブタイプの多型とパラメーター多型の主な違いは何ですか?
サブタイプの多型はJavaでどのように機能しますか?
Javaでは、継承とインターフェイスを使用することにより、サブタイプの多型が達成されます。スーパークラス参照変数は、サブクラスオブジェクトを指すことができます。これにより、Javaは実行時にどの方法を呼び出すかを決定することができます。これは動的メソッドスケジューリングと呼ばれます。これは、Javaの強力な特徴の1つであり、動的な多型をサポートできます。もちろん、Javaの簡単な例を考えてみましょう。 「動物」と呼ばれるスーパークラスと2つのサブクラス「犬」と「猫」があるとします。 「犬」と「猫」のクラスの両方が、「動物」クラスの「サウンド」方法を書き直します。これで、「犬」または「猫」のオブジェクトを指す「動物」参照を作成し、「サウンド」メソッドを呼び出すと、Javaは実行時にどのクラスの「サウンド」メソッドを呼び出すかを決定します。これは、サブタイプの多型の例です。
サブタイプの多型は、オブジェクト指向プログラミングの基本的な側面です。これにより、コードの柔軟性と再利用性が可能になります。サブタイプの多型を使用して、一連のクラスの共通インターフェイスを設計し、このインターフェイスを使用して、それらのクラスのオブジェクトと統一された方法で対話できます。これにより、コードをよりクリーンで直感的で容易にしやすくなります。
リスコフ代替原理(LSP)は、プログラムが基本クラスを使用している場合、プログラムがそれを知らずにサブクラスを使用できるはずであると述べているオブジェクト指向設計の原理です。言い換えれば、スーパークラスのオブジェクトは、プログラムの正しさに影響を与えることなく、サブクラスのオブジェクトに置き換えることができるはずです。サブタイプの多型は、LSPの直接的な応用です。
いいえ、すべてのプログラミング言語がサブタイプの多型をサポートしているわけではありません。これは主に、Java、C、C#などの静的に型付けられたオブジェクト指向プログラミング言語の機能です。 PythonやJavaScriptのような動的に型付けされた言語は、Duckタイプと呼ばれるさまざまな形態の多型を持っています。
サブタイプの多型におけるアップコンバージョンの概念を説明できますか?
サブタイプの多型のコンテキストでのダウンコンバージョンとは何ですか?
サブタイプの多型は、コードの再利用性をどのように促進しますか?
以上がサブタイプの多型 - 実行時に実装を交換しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。