1. コンセプト
遅延ロード: オブジェクトには必要なデータがすべて含まれているわけではありませんが、データの取得方法はわかっています。
遅延ロードは、データベースの消費を減らすために必要なときにデータベースからデータを取得することです。しかし、まだたくさんのトリックが含まれています。
2. 遅延読み込みを実装する
遅延読み込みを実装するには、遅延初期化、仮想プロキシ、値ホルダー、ゴースティングの 4 つの主な方法があります。
(1) 遅延初期化
1.1 概念
これは最も簡単な方法です。これは、属性フィールドにアクセスするたびに、まずフィールドが空かどうかを確認し、空の場合はこのフィールドの値を取得する必要があることを意味します。これにより、クラス内からであっても、このフィールドへのすべてのアクセスが取得メソッドを通じて達成される必要があります。
class Supplier{ private $products; public function getProducts(){ if($products == null) $products = $Product->findForSupplier(); return $products; } }
1.3 いつ使用するか
ドメインで追加のデータベース アクセスが必要な場合にのみ、遅延読み込みの使用を検討してください。
追加の呼び出しが必要で、メインオブジェクトの使用時に呼び出されるデータが使用されない場合。
アクティビティ記録、テーブルデータエントリ、行データエントリに最適です。
(2) 仮想プロキシ
2.1 概念
は本質的にオブジェクトであり、そのメソッドの 1 つが呼び出された場合にのみ、データベースから適切なオブジェクトをロードします。
簡単に言うと、オブジェクトのプロキシ オブジェクトです。オブジェクトは初期化中にロードされず、プロキシ オブジェクトが呼び出されたときにのみロードされます。
/** * 虚代理,只有在被访问成员时才调用闭包函数生成目标对象。 */ class VirtualProxy { private $holder = null; private $loader = null; /** * @param Closure $loader 生成被代理对象的闭包函数 */ public function __construct(Closure $loader) { $this->loader = $loader; } /** * 代理成员方法的调用 * * @param string $method * @param array $arguments * @throws BadMethodCallException * @return mixed */ public function __call($method, array $arguments = null) { $this->check(); if (!method_exists($this->holder, $method)) { throw new BadMethodCallException(); } return call_user_func_array( array(&$this->holder, $method), $arguments); } /** * 代理成员属性的读取 * * @param string $property * @throws ErrorException * @return mixed */ public function __get($property) { $this->check(); if (!isset($this->holder->$property)) { throw new ErrorException(); } return $this->holder->$property; } /** * 代理成员属性的赋值 * * @param string $property * @param mixed $value */ public function __set($property, $value) { $this->check(); $this->holder->$property = $value; } /** * 检查是否已经存在被代理对象,不存在则生成。 */ private function check() { if (null == $this->holder) { $loader = $this->loader; $this->holder = $loader(); } } } // 测试 $v = new VirtualProxy(function(){ echo 'Now, Loading', "\n"; $a = new ArrayObject(range(1,100)); $a->abc = 'a'; // 实际使用中,这里调用的是 DataMapper 的 findXXX 方法 // 返回的是领域对象集合 return $a; }); // 代理对象直接当作原对象访问 // 而此时构造方法传入的 callback 函数才被调用 // 从而实现加载对象操作的延迟 echo $v->abc . $v->offsetGet(50);
(3) 値ホルダー (値ホルダー)
3.1 概念
他のオブジェクトをラップするために使用されるオブジェクトです。基本オブジェクトを取得するには、値ホルダーにアクセスしてその値を取得できますが、最初の値のみを取得します。値ホルダーにアクセスすると、実際にデータベースからデータが読み取られます。
(4) Ghost
4.1 概念
オブジェクトがデータベースからロードされるとき、そのオブジェクトにはその ID のみが含まれます。ドメインにアクセスするたびに、その完全な状態がロードされます。ゴースティングは、各フィールドが一度に遅延初期化されるオブジェクト、またはオブジェクト自体が仮想プロキシとなる仮想プロキシとして考えられます。
//继承要加载的对象 class DeferredEventCollection extends EventCollection { private $stmt; private $valueArray; private $run=false;//标识当前加载状态 //构造方法,不真正获取数据,只包含其$valueArray(ID) function __construct( Mapper $mapper, \PDOStatement $stmt_handle,array $valueArray ) { parent::__construct( null, $mapper ); $this->stmt = $stmt_handle; $this->valueArray = $valueArray; } //加载完全状态 function notifyAccess() { if ( ! $this->run ) { $this->stmt->execute( $this->valueArray ); $this->raw = $this->stmt->fetchAll(); $this->total = count( $this->raw ); } $this->run=true; } }
3. 読み込み遅延のリスク
1. 継承により読み込み遅延の問題が発生することがよくあります。ゴースティングを使用したい場合は、どのタイプのゴーストを作成するかを知る必要がありますが、データが正しくロードされていない場合は、それを判断するのが難しいことがよくあります。仮想プロキシは、静的に型付けされた言語でも同じ問題に悩まされます。
2. 遅延読み込みにより、必要以上のデータベース アクセスが発生しやすくなります。本の中での別名は「変動負荷」です。たとえば、遅延読み込みを使用してコレクションにデータを設定し、一度に 1 つの要素のみにアクセスします。このようにして、必要なすべてのオブジェクトを一度に読み出すのではなく、オブジェクトがアクセスされるたびにデータベースにアクセスします。このロード方法はシステムのパフォーマンスに重大な影響を与えます。もちろん、遅延ロードされたコレクションを使用する代わりに、クラス コレクション自体を遅延ロードし、クラス コレクションをロードするときにコンテンツ全体を一度にロードすることもできます。
4. まとめ
遅延読み込みはアスペクト指向 (AOP) プログラミングに非常に適しています。遅延読み込み操作は別のアスペクトに配置できるため、遅延読み込み戦略を個別に変更でき、ドメイン開発者は遅延読み込みの問題に対処する必要がありません。
遅延読み込みコードをドメイン クラスに明示的に追加するかどうかに関係なく、遅延読み込みは良い習慣です。型安全性に加えて、配列の代わりにコレクション オブジェクトを使用する利点は、遅延読み込みを使用できることです。