代理模式和享元模式是两种完全不同目标的设计模式, 核心却别体现在解决的问题\核心思想和应用场景上. 我们可以结合Unity的例子, 从多个维度对比:
 

1. 核心目标不同

  • 代理模式: 核心是”控制对象访问”, 通过代理为原对象添加额外功能(如权限校验\日志\缓存\延迟加载等), 同时保持接口一致, 让客户端无感知.
  • 享元模式: 核心是”共享相似对象节省内存”, 通过分离”内部状态”(可共享的固定部分)和”外部状态”(随实例变化的部分), 减少对象创建数量, 避免重复加载资源.
 
简单说: ”大家长得差不多, 把一样的部分共享, 不一样的部分各自带, 省点内存.”
 

2. 解决的问题不同

  • 代理模式解决的问题: 当需要在不修改原对象的前提下, 对其访问添加附加逻辑(如”加载资源前显示UI””校验权限””记录日志”), 或控制访问时机(如”延迟加载大资源”)时, 用代理模式. 例如: 加载一个大型场景时, 需要先显示”加载中”UI, 加载完成后记录耗时, 重复加载时直接用缓存 ——— 这些附加逻辑不应该写在 “真正的加载器” 里, 而是用代理包装.
  • 享元模式解决的问题: 当系统种存在大量相似对象(如10000棵树\1000个雨滴粒子), 且这些对象大部分数据(模型\材质等)是重复的, 导致内存占用过高时, 用享元模式. 例如: 10000棵松树的模型\材质完全相同(内部状态), 只有位置\旋转不同(外部状态) ——— 通过享元共享一份模型资源, 避免10000次重复加载.
 

3. 结构与角色不同

代理模式的核心结构:
  • 抽象主题(Subject): 原对象和代理的统一接口(如 IResourceLoader ).
  • 真实主题(Real Subject): 被代理的对象, 实现核心功能(如 RealResourceLoader 负责实际加载)
  • 代理(Proxy): 实现抽象主题, 持有真实主题的引用, 在调用真实主题前后添加附加逻辑(如 ResourceLoaderProxy 处理UI\日志\缓存).
 
核心关系: 代理“包装”真实对象, 客户端通过代理访问真实对象。
 
享元模式的核心结构:
  • 抽象享元(Flyweight): 定义共享对象接口(如 ItreeFlyweight )
  • 具体享元(ConcreteFlyweight): 存储可共享的内部状态(如 PineTreeFlyweight 持有松树模型)
  • 享元工厂(Flyweight Factory): 管理享元, 确保相同内部状态的享元只被创建一次(如 TreeFlyweightFactory 缓存松树\橡树享元).
  • 外部状态: 客户端传入的\随实例变化的数据(如树的位置\旋转), 各自携带外部状态
 
核心关系: 多个实例共享同一个享元的内部状态,各自携带外部状态
 

4. 应用场景对比(Unity实例)

维度
代理模式(Unity场景)
经典场景
资源加载(UI\日志\权限)\网络请求(加重试\加密)\对象池控制访问
核心操作
对原对象的访问”加前置/后置逻辑”(如加载前显示UI)
对象数量变化
不会减少对象数量(代理和真实对象是1:1关系)
内存影响
可能增加少量内存(代理对象本身), 但不优化核心资源内存
 

5. 一句话总结区别

  • 代理模式是 “访问控制器”: 帮原对象 “揽活儿”, 顺便加功能, 但不改变原对象的核心角色
  • 享元模式是 “资源共享器”: 让相似对象”拼单”, 共享重复资源, 减少内存浪费
 
比如:
  • 用代理模式的资源加载器, 重点是 “加载前后做了什么额外操作”;
  • 用享元模式的树木系统, 重点是”10000 棵树如何只加载1份模型”