知识卡片

延迟加载的核心思想与四种实现方式概览

结构图卡

内容

为了方便开发者,加载一个对象时顺带自动加载与之相关的对象是很自然的设计,但顺着这个逻辑走到底,会导致加载一个对象牵出一大批相关对象、而真正需要的可能只有寥寥几个,白白拖累性能。延迟加载暂停这个过度加载的过程:在对象结构里设个标记,让真正用到某份数据时才去加载它——用作者的话说,”如果你懒得做一些事情,当事后证明这些事情根本不需要做的时候你就赚了”。实现延迟加载主要有四种方式。延迟初始化:每次访问某个域前先检查是否为空,为空就现算/现加载再返回,最简单,但会在对象和数据库间强加一层依赖,最适合活动记录、表数据入口、行数据入口这类本来就和数据库耦合的场景。虚代理:放一个”看起来像目标对象、实际什么都没装”的代理对象在域里,只有它的某个方法被调用时才去数据库加载真正的对象——优点是几乎无缝,缺点是代理和真实对象是两个不同的实例,容易在相等性判断上栽跟头,且静态类型语言里可能得为每个被代理的类都造一个代理类。值保持器:专门包一层来持有目标对象,第一次访问值保持器时才真正从数据库读取——代价是宿主类需要知道值保持器的存在,牺牲了强类型的显式性,但能避免虚代理的标识问题。重影:加载对象时先只填充它的ID,各个域第一次被访问时才各自触发加载,可以把重影理解成”整个对象本身就是自己的虚代理”,好处是可以直接把它放进标识映射里,天然避免循环引用问题。

结构图

flowchart TB
  A["延迟加载四种实现"]
  A --> B["延迟初始化<br/>访问前判空<br/>适合活动记录/表数据入口"]
  A --> C["虚代理<br/>假对象占位<br/>调用时才加载<br/>需处理相等性问题"]
  A --> D["值保持器<br/>包一层持有目标<br/>首次访问才读取"]
  A --> E["重影<br/>先加载ID<br/>各域按需触发加载<br/>可直接放入标识映射"]

参考来源

- 位置:《企业应用架构模式》第二部分"模式"之"第11章 对象-关系行为模式"之"11.3 延迟加载"(源文件:_epub-src/OEBPS/Text/000098.html) - 结论依据:原文说明"众所周知,如果你懒得做一些事情,当事后证明这些事情根本就不需要做的时候你就赚了……主要有四种实现延迟加载的方法:延迟初始化、虚代理、值保持器和重影",并逐一介绍每种方式的运行机制、优缺点和适用场景,直接支撑本卡结构图。 - 原始内容:重影是部分状态下的真实对象。当从数据库加载对象的时候,它只包含其ID。当每次要访问某个域时,它就会加载其完全状态……如果使用重影,可以直接把它放到它的标识映射中。这种方式可以维护标识和避免所有在读数据时出现的循环引用带来的问题。