Home >Backend Development >PHP Tutorial >Object-relational behavior pattern lazy loading
1. Concept
Lazy Load: An object, although it does not contain all the data required, but it knows how to obtain the data.
Lazy loading seems to be very simple. It is to obtain the data from the database when it is needed to reduce the consumption of the database. But there are still a lot of tricks involved.
2. Implement lazy loading
There are four main methods to implement Lazy Load: delayed initialization, virtual proxy, value holder and ghosting.
(1) Lazy initialization
1.1 Concept
This is the simplest method. This means that every time you access an attribute field, you must first check whether the field is empty. If it is empty, then obtain the value of this field. This must ensure that all access to this field, even from within the class, must be achieved through the acquisition method.
class Supplier{ private $products; public function getProducts(){ if($products == null) $products = $Product->findForSupplier(); return $products; } }
1.3 When to use
Consider using lazy loading only when the domain requires additional database access.
Requires additional calls, and when the data called when using the main object is not used.
Most suitable for activity records, table data entries and row data entries.
(2) Virtual proxy
2.1 Concept
is essentially an object and does not contain anything. Only when one of its methods is called, it loads the appropriate object from the database.
To put it simply, it is a proxy object of an object. The object is not loaded during initialization. It is only loaded when the proxy object is called.
/** * 虚代理,只有在被访问成员时才调用闭包函数生成目标对象。 */ 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) Value holder (value holder)
3.1 Concept
An object used to wrap some other object. To get the base object, you can access the value holder to get its value, but only the first The first time the value holder is accessed it actually reads the data from the database.
(4) Ghost
4.1 Concept
A real object in partial state. When the object is loaded from the database, it only contains its ID. Every time a domain is accessed, it loads its full state. Think of a ghost as an object, with each of its fields lazily initialized all at once, or as a virtual proxy, with the object itself being its virtual proxy.
//继承要加载的对象 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. Risks of delayed loading
1. Inheritance often causes problems with delayed loading. If you want to use ghosting, you need to know what type of ghost you want to create, which is often difficult to tell if the data is not loaded correctly. Virtual proxies suffer from the same problem in statically typed languages.
2. Lazy loading can easily lead to more database access than required. Alias in the book "fluctuation loading". For example, use lazy loading to populate a collection, and then only access one element at a time. In this way, the database will be accessed every time an object is accessed, instead of reading out all the required objects at once. This loading method will seriously affect the performance of the system. Of course, instead of using lazy-loaded collections, you can make the class collection itself lazy-loaded, loading the entire content at once when loading the class collection.
4. Summary
Lazy loading is very suitable for aspect-oriented (AOP) programming. The lazy loading operation can be placed in a separate aspect, so that the lazy loading strategy can be changed independently, and domain developers do not have to deal with lazy loading issues.
Regardless of whether you add lazy loading code explicitly in domain classes, lazy loading is a good practice. In addition to type safety, the benefit of using a collection object instead of an array is that you can use lazy loading.