`

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

阅读更多

形象化设计模式实战     HELLO!架构

 

一、概念

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

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

 

 

二、实现延迟加载

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

 

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

 

1.1 概念

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

 

1.2 代码实现

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

 

1.3 使用时机

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

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

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

 

 

(2)虚代理(virtual proxy)

 

2.1 概念

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

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

 

2.2 代码实现

/**
 * 虚代理,只有在被访问成员时才调用闭包函数生成目标对象。
 */
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。当每次要访问某个域时,它就会加载其完全状态。把重影看成一个对象,它的每个域都是被一下子延迟初始化的,或者把它看成一个虚代理,对象本身就是它的虚代理。

 

4.2 代码实现

//继承要加载的对象
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)的程序设计。可以将延迟加载操作置于一个单独的方面,这样能独立改变延迟加载策略,领域开发者也不必处理延迟加载的问题了。

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

2
1
分享到:
评论

相关推荐

    hibernate延迟加载解决

    - 避免在`equals()`和`hashCode()`方法中使用延迟加载属性,因为这可能导致意外的加载行为。 - 考虑使用“批处理”(batch-size)属性来优化延迟加载,一次性加载多个关联对象,减少数据库交互次数。 综上所述,...

    延迟加载、迫切加载的用法

    在后端开发中,ORM(对象关系映射)如Hibernate支持延迟加载和迫切加载的配置;在数据库设计中,SQL的JOIN操作可以根据需求选择延迟或迫切加载关联数据。 总之,延迟加载和迫切加载是优化资源管理、提升性能的重要...

    面向对象的23种设计模式

    责任链模式允许多个对象有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。 14. **命令模式(Command Pattern)**: 命令模式将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对...

    iOS 延迟加载

    在iOS开发中,延迟加载(Lazy Loading)是一种优化策略,用于推迟对象的初始化或加载,直到它们真正被需要时才进行。这种技术可以显著减少应用程序启动时的内存占用,提高性能,尤其是对于大型或者资源密集型的应用...

    面向对象设计模式

    行为型模式着重于对象之间的交互和职责分配,比如观察者模式(Observer)定义了对象间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新;责任链模式(Chain of ...

    设计模式:可复用的面向对象软件的基础

    - 代理模式:用于控制对游戏资源的访问,如延迟加载或异步加载资源。 通过应用设计模式,游戏引擎开发者能够更好地实现软件的复用性、可维护性,以及更好的软件质量。学习和掌握设计模式,对于开发高质量、高性能、...

    Hibernate延迟加载

    在传统的Eager Loading(急切加载)模式下,一旦加载了一个对象,与之相关的所有关联对象也会立即被加载到内存中,这种做法虽然方便了对象的访问,但在大量数据处理时会造成资源浪费和性能下降。 相比之下,**延迟...

    无法加载设计模式 APE

    如果"APE"设计模式属于创建型模式,那么它可能与对象的实例化和管理有关,可能会涉及到动态配置或者延迟加载等概念。如果是结构型模式,可能涉及类的组合或继承结构的优化。若为行为型模式,可能关乎对象间通信和...

    设计模式可复用面向对象软件的基础100406_1

    例如,通过策略模式可以避免大量条件分支,使用工厂模式可以简化对象创建,利用装饰模式可以在不修改原有类的情况下扩展功能,单例模式保证全局只有一个实例,而代理模式可以用于延迟加载或者权限控制等。...

    面向对象设计的设计模式

    14. 状态模式:状态模式允许对象在其内部状态改变时改变其行为,对象看起来似乎修改了它的类。 15. 命令模式:命令模式将请求封装为一个对象,以便使用不同的请求、队列请求、或者支持可撤销的操作。 这些设计模式...

    C#面向对象设计模式纵横谈(13):Proxy 代理模式(结构型模式) (Level 300)

    代理模式可以用来延迟加载、安全控制、监控行为等。 2. **代理模式的角色**: - **真实对象(Real Subject)**:这是代理模式所代理的实际业务对象,它实现了与代理对象相同的接口,以便代理对象可以调用真实对象...

    ef三种加载方式.docx

    Entity Framework (EF) 是一种流行的 .NET ORM (对象关系映射) 框架,它允许开发者使用面向对象的方式来操作数据库。在 EF 中,关联实体的加载有三种主要方式:延迟加载(Lazy Loading)、预先加载(Eager Loading)和...

    java设计模式ppt

    抽象工厂模式同样是创建型模式之一,它提供了一个接口来创建一系列相关或相互依赖的对象,而无需指定它们的具体类。该模式适用于当一个系统需要创建一组相关的对象,但具体类不确定的情况。抽象工厂模式可以有效地...

    二十一种设计模式java例子

    观察者模式是一种行为模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并被自动更新。代理模式则为其他对象提供一种代理以控制对这个对象的访问,它可以用于...

    设计模式之代理模式proxy

    **设计模式之代理模式(Proxy Pattern)** 设计模式是软件工程中的一种最佳实践,它是在特定情境下解决常见问题的模板。代理模式是其中一种行为设计模式,它的核心思想是为一个对象提供一个替身或者代理,以控制对...

    JAVA设计模式之禅+源代码.rar

    6. **代理模式**:提供一个代理对象来控制对原对象的访问,可以用于延迟加载、安全控制、统计等功能。Java的动态代理(`java.lang.reflect.Proxy`)是代理模式的一个典型应用。 7. **适配器模式**:将两个不兼容的...

    设计模式学习笔记总结

    这里我们聚焦于C#语言中的设计模式学习笔记,涵盖了多种经典的设计模式,如合成模式、桥梁模式、装饰模式、享元模式、门面模式、命令模式、工厂方法、策略模式、代理模式以及状态模式。下面将对这些模式逐一进行详细...

    Java设计模式之代理模式(结构)

    虚拟代理模式是一种特殊的代理模式,主要目的是延迟昂贵对象的创建时间,以减少对系统资源的消耗。该模式适用于那些创建成本较高或者处理过程较为复杂的情况,例如大型图像处理、大数据量计算等场景。 #### 三、...

    C++设计模式.pdf

    ### 行为模式 #### Template Method模式 Template Method模式定义了一个操作中的算法骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 #### Strategy模式 ...

Global site tag (gtag.js) - Google Analytics