0

0

对象关系行为模式之延迟加载

巴扎黑

巴扎黑

发布时间:2016-11-21 10:17:35

|

1330人浏览过

|

来源于php中文网

原创

一、概念

Lazy Load:一个对象,它虽然不包含所需要的所有数据,但是知道怎么获取这些数据。

延迟加载貌似很简单,就是在数据需要时再从数据库获取,减少数据库的消耗。但这其中还是有不少技巧的。

二、实现延迟加载

实现Lazy Load主要有四种方法:延迟初始化、虚代理、值保持器和重影。

(1)延迟初始化(Lazy initialization)

 1.1 概念

这个是最简单的方法。意思就是每次访问属性域都要先检查该域是否为空,如果为空,再获取这个域的值。这样必须做到所有对该域的访问,即使来自类的内部,都要通过获取方法来实现。

class Supplier{
  private $products;
  public function getProducts(){
    if($products == null)
      $products = $Product->findForSupplier();
    return $products;
  }
}

1.3 使用时机

只有在域需要另外的数据库访问时才考虑使用延迟加载。

需要额外的调用,并且当使用主对象时所调用的数据没有用到的时候。

最适用于活动记录、表数据入口和行数据入口。

(2)虚代理(virtual proxy)

 

2.1 概念

实质为一对象,不包含任何东西,只有当它的一个方法被调用时,它才从数据库加载恰当的对象。

问问小宇宙
问问小宇宙

问问小宇宙是小宇宙团队出品的播客AI检索工具

下载

说简单点说是一个对象的代理对象,初始化时不加载对象,只有当代理对象被调用方法时才真正去加载。

/**
 * 虚代理,只有在被访问成员时才调用闭包函数生成目标对象。
 */
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)

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;
    }
}

三、延迟加载的风险

1、继承常常会给延迟加载带来问题。如果要用重影,需要知道要创建什么类型的重影,如果没有正确加载数据,往往就很难说了。虚代理在静态数据类型语言中也会遇到同样的问题。

2、延迟加载容易导致产生超出需要的数据库访问。书中别名“波动加载”。如用延迟加载来填充一个集合,然后每次只访问其中一个元素。这样就会每访问一个对象就访问一次数据库,而不是一次把所需对象全部读出来。这种加载方式会严重影响系统的性能。当然,可以不使用延迟加载的集合,而使类集合本身成为延迟加载,加载类集合时一次加载全部内容。

四、小结

延迟加载很适合于面向方面(AOP)的程序设计。可以将延迟加载操作置于一个单独的方面,这样能独立改变延迟加载策略,领域开发者也不必处理延迟加载的问题了。

无论你是否在领域类中显式地添加延迟加载代码,延迟加载都是一个好习惯。除了类型安全之外,使用集合对象而非数组的好处是你可以使用延迟加载。

相关专题

更多
php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

200

2025.12.31

php网站源码教程大全
php网站源码教程大全

本专题整合了php网站源码相关教程,阅读专题下面的文章了解更多详细内容。

106

2025.12.31

视频文件格式
视频文件格式

本专题整合了视频文件格式相关内容,阅读专题下面的文章了解更多详细内容。

122

2025.12.31

不受国内限制的浏览器大全
不受国内限制的浏览器大全

想找真正自由、无限制的上网体验?本合集精选2025年最开放、隐私强、访问无阻的浏览器App,涵盖Tor、Brave、Via、X浏览器、Mullvad等高自由度工具。支持自定义搜索引擎、广告拦截、隐身模式及全球网站无障碍访问,部分更具备防追踪、去谷歌化、双内核切换等高级功能。无论日常浏览、隐私保护还是突破地域限制,总有一款适合你!

90

2025.12.31

出现404解决方法大全
出现404解决方法大全

本专题整合了404错误解决方法大全,阅读专题下面的文章了解更多详细内容。

654

2025.12.31

html5怎么播放视频
html5怎么播放视频

想让网页流畅播放视频?本合集详解HTML5视频播放核心方法!涵盖<video>标签基础用法、多格式兼容(MP4/WebM/OGV)、自定义播放控件、响应式适配及常见浏览器兼容问题解决方案。无需插件,纯前端实现高清视频嵌入,助你快速打造现代化网页视频体验。

19

2025.12.31

关闭win10系统自动更新教程大全
关闭win10系统自动更新教程大全

本专题整合了关闭win10系统自动更新教程大全,阅读专题下面的文章了解更多详细内容。

15

2025.12.31

阻止电脑自动安装软件教程
阻止电脑自动安装软件教程

本专题整合了阻止电脑自动安装软件教程,阅读专题下面的文章了解更多详细教程。

5

2025.12.31

html5怎么使用
html5怎么使用

想快速上手HTML5开发?本合集为你整理最实用的HTML5使用指南!涵盖HTML5基础语法、主流框架(如Bootstrap、Vue、React)集成方法,以及无需安装、直接在线编辑运行的平台推荐(如CodePen、JSFiddle)。无论你是新手还是进阶开发者,都能轻松掌握HTML5网页制作、响应式布局与交互功能开发,零配置开启高效前端编程之旅!

3

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
nginx浅谈
nginx浅谈

共15课时 | 0.8万人学习

前端开发(基础+实战项目合集)
前端开发(基础+实战项目合集)

共60课时 | 3.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号