代理模式核心是延迟加载,需通过接口统一、懒初始化、方法转发实现; userdataproxy 应仅存 $id,getrealinstance() 中判 null 并赋值,各业务方法必须调用真实实例对应方法,避免构造即执行导致无意义 io。

代理模式不是靠背概念学会的,是靠在真实场景里反复“拆”和“装”接口才真正理解的。你不需要先搞懂所有分类(虚拟/保护/远程),先从 UserDataProxy 这种最常写的、带延迟加载的代理类开始练手,能跑通、能 debug、能说出“为什么这里不能 new 实例”就算入门了。
怎么写一个可用的 UserDataProxy 类
核心就三点:接口统一、延迟实例化、方法转发。别一上来就加缓存或日志,先让代理能透明替换真实对象。
-
UserDataProvider接口必须定义所有要暴露的方法,比如getName()、getEmail(),缺一个就会报Fatal error: Call to undefined method -
UserDataProxy::__construct()里只存$id,不调new UserData($id) -
getRealInstance()方法里做懒加载判断,且只初始化一次,用null === $this->realInstance检查,别用empty()(对象实例会被判为 true) - 每个业务方法(如
getName())都必须调$this->getRealInstance()->getName(),不能漏掉->或写错方法名
为什么 new UserData($id) 不能放在构造函数里
因为构造即执行 —— 只要 new 出来,不管后续用不用 getName(),数据库查询或远程调用就已经发生了。尤其在循环中批量处理用户 ID 时,foreach ($ids as $id) { new UserDataProxy($id); } 看似没调方法,其实已经触发 N 次无意义 IO。
- 真实对象创建开销大(比如解析 5MB JSON、连 Redis 查 10 层嵌套关系)才值得上代理
- 如果
UserData构造只是简单赋值,加代理反而多一层函数调用,拖慢性能 - 代理类本身也有内存占用,
$this->realInstance缓存的是对象引用,不是深拷贝,但累积多了也吃内存
常见报错和排查点
写完跑不起来?90% 是这三类问题:
-
Fatal error: Call to undefined method UserDataProxy::getEmail()→ 检查UserDataProvider接口是否声明了该方法,以及UserData是否实现了它 -
Maximum function nesting level of '256' reached→ 代理方法里不小心写成$this->getName()(自己调自己),而不是$this->getRealInstance()->getName() - 数据总是空或没更新 →
getRealInstance()返回了新实例但没赋给$this->realInstance,漏了$this->realInstance = ...这句赋值
代理模式真正的难点不在写法,而在于判断“什么时候该用”。它不解决数据一致性、事务或并发问题,也不替代 ORM 的关联加载逻辑;它只管一件事:把“创建对象”这个动作,从“声明变量”时刻,推迟到“第一次真正需要它的时候”。这点没想清楚,代码越写越绕。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











