当前位置: 首页 > news >正文

Java 23 种设计模式:从踩坑到精通 | 番外:代理模式 —— 物流服务访问控制实战

Java 23 种设计模式:从踩坑到精通 | 番外:代理模式 —— 物流服务访问控制实战

摘要:代理模式为其他对象提供一种代理以控制对这个对象的访问,可以在不修改原代码的情况下添加权限校验、日志记录、缓存加速、延迟加载等横切逻辑。本文结合**智能物流中“远程物流服务调用”**的场景,完整展示如何用代理类控制对真实物流服务的访问,并与装饰器模式深度对比,帮你掌握“控制访问”的设计精髓。

🗺️本文阅读地图(3 分钟速览)

  • 为什么每次调用远程物流服务都要经过权限校验?
  • ✅ 代理模式核心角色:抽象主题、真实主题、代理
  • 手写物流服务代理:权限校验 + 日志 + 缓存 + 延迟加载
  • 🚀 代理 vs 装饰器:控制访问 vs 增强功能
  • 🎤 面试必问:“代理模式和装饰器模式有什么区别?Spring AOP 用了哪个?”

📖《Java 23 种设计模式:从踩坑到精通》
开篇:系列介绍与目录 | 正篇:代理模式 Proxy —— 你的 AOP 就是用代理实现的 |当前:番外 · 代理模式 × 物流服务访问控制
🔗 返回系列总目录

1. 物流远程服务调用的痛点

在物流系统中,真实的物流服务部署在远程服务器上,每次调用需要建立网络连接、进行权限校验、记录访问日志。如果把这些横切逻辑直接写在业务代码中,业务代码会变得臃肿且难以维护。更麻烦的是,真实物流服务的初始化成本很高(建立连接、加载配置),如果每次调用都重新初始化,会严重影响性能。

代理模式的解决思路:创建一个代理类,实现与真实物流服务相同的接口,在代理类中添加权限校验、日志、缓存等横切逻辑,并延迟加载真实服务对象。客户端只与代理交互,完全不知道代理的存在。

1.1 你的场景该不该用代理?

判断标准是 → 用代理否 → 用其他方式
需要在不修改原代码的情况下控制访问
需要添加横切逻辑(日志/事务/缓存/权限)
真实对象创建成本高,需要延迟加载
只是想给对象增加新功能,且希望自由组合用装饰器模式
需要转换接口用适配器模式

2. 代理模式 UML(物流服务访问控制场景)


3. 完整源码实现

3.1 抽象主题:物流服务接口 (ShippingService)

/** * 抽象主题:物流服务接口 */publicinterfaceShippingService{doublecalculateFee(doubleweight,doubledistance);StringtrackOrder(StringorderId);}

💬白话:这是客户端和代理、真实服务之间的“合同”——大家都实现这个接口,客户端只管调用,不关心背后是谁在执行。

3.2 真实主题:远程物流服务 (RealShippingService)

/** * 真实主题:真实物流服务(模拟远程服务) */publicclassRealShippingServiceimplementsShippingService{publicRealShippingService(){// 模拟连接远程服务的耗时操作System.out.println(" 🔗 正在连接远程物流服务器...");try{Thread.sleep(500);}catch(InterruptedExceptione){Thread.currentThread().interrupt();}System.out.println(" ✅ 远程物流服务已连接\n");}@OverridepublicdoublecalculateFee(doubleweight,doubledistance){System.out.println(" 📦 [RealService] 正在计算运费...");doublebaseRate=5.0;doublefee=baseRate+weight*2.5+distance*0.8;returnMath.round(fee*100.0)/100.0;}@OverridepublicStringtrackOrder(StringorderId){System.out.println(" 📦 [RealService] 正在查询物流轨迹...");returnString.format("订单[%s]:已发货 → 运输中 → 预计2天后到达",orderId);}}

💬白话:真实物流服务的初始化非常耗时——要连接远程服务器、加载配置。如果用代理延迟加载,第一次调用时才初始化,能大幅提升启动速度。

3.3 代理类:物流服务代理 (ShippingServiceProxy)

importjava.util.HashMap;importjava.util.Map;/** * 代理类:物流服务代理 * 实现权限校验、日志记录、缓存、延迟加载 */publicclassShippingServiceProxyimplementsShippingService{privateRealShippingServicerealService;// 持有真实对象的引用privateStringapiKey;privateintrequestCount;privateMap<String,Object>cache;publicShippingServiceProxy(StringapiKey){this.apiKey=apiKey;this.requestCount=0;this.cache=newHashMap<>();System.out.println(" 🛡️ 物流代理已初始化(延迟加载:尚未连接真实服务)\n");}/** * 延迟加载真实服务对象 */privateRealShippingServicegetRealService(){if(realService==null){realService=newRealShippingService();}returnrealService;}/** * 权限校验 */privatebooleancheckPermission(){if(apiKey==null||apiKey.isEmpty()){System.out.println(" ❌ 权限校验失败:API Key 为空");returnfalse;}if(!apiKey.startsWith("LOGISTICS_")){System.out.println(" ❌ 权限校验失败:无效的 API Key");returnfalse;}System.out.println(" ✅ 权限校验通过");returntrue;}/** * 记录访问日志 */privatevoidlogAccess(Stringmethod,Objectparams){requestCount++;System.out.printf(" 📝 [日志] 第%d次访问 | 方法:%s | 参数:%s\n",requestCount,method,params);}/** * 缓存结果 */privatevoidcacheResult(Stringkey,Objectvalue){cache.put(key,value);System.out.printf(" 💾 [缓存] 已缓存结果:key=%s\n",key);}/** * 从缓存获取结果 */privateObjectgetCachedResult(Stringkey){if(cache.containsKey(key)){System.out.printf(" ⚡ [缓存命中] 直接返回缓存结果:key=%s\n",key);returncache.get(key);}returnnull;}@OverridepublicdoublecalculateFee(doubleweight,doubledistance){logAccess("calculateFee",String.format("weight=%.1f, distance=%.1f",weight,distance));if(!checkPermission()){thrownewSecurityException("无权访问物流服务");}StringcacheKey="fee_"+weight+"_"+distance;Objectcached=getCachedResult(cacheKey);if(cached!=null)return(double)cached;doubleresult=getRealService().calculateFee(weight,distance);cacheResult(cacheKey,result);returnresult;}@OverridepublicStringtrackOrder(StringorderId){logAccess("trackOrder",orderId);if(!checkPermission()){thrownewSecurityException("无权访问物流服务");}StringcacheKey="track_"+orderId;Objectcached=getCachedResult(cacheKey);if(cached!=null)return(String)cached;Stringresult=getRealService().trackOrder(orderId);cacheResult(cacheKey,result);returnresult;}publicintgetRequestCount(){returnrequestCount;}}

💬白话:代理类就像前台的“门禁”——先验权限、再记日志、然后看缓存有没有,最后才放行给真实服务。真实服务只初始化一次getRealService()),后续调用直接复用。

3.4 客户端测试

publicclassClient{publicstaticvoidmain(String[]args){System.out.println("=== 智能物流远程服务代理(代理模式)===\n");ShippingServiceshippingService=newShippingServiceProxy("LOGISTICS_12345");// 场景1:首次调用(触发真实服务连接 + 缓存)System.out.println("【场景1】首次计算运费");System.out.println(shippingService.calculateFee(10.5,150.0)+"\n");// 场景2:相同参数再次调用(缓存命中)System.out.println("【场景2】再次计算相同运费");System.out.println(shippingService.calculateFee(10.5,150.0)+"\n");// 场景3:查询物流轨迹(首次,无缓存)System.out.println("【场景3】查询物流轨迹");System.out.println(shippingService.trackOrder("ORD20260723001")+"\n");// 场景4:再次查询相同订单(缓存命中)System.out.println("【场景4】再次查询相同订单");System.out.println(shippingService.trackOrder("ORD20260723001")+"\n");// 场景5:不同参数计算运费(缓存未命中)System.out.println("【场景5】计算不同运费");System.out.println(shippingService.calculateFee(5.0,80.0)+"\n");System.out.println("总请求次数:"+((ShippingServiceProxy)shippingService).getRequestCount());}}

4. 运行结果

=== 智能物流远程服务代理(代理模式)=== 🛡️ 物流代理已初始化(延迟加载:尚未连接真实服务) 【场景1】首次计算运费 📝 [日志] 第1次访问 | 方法:calculateFee | 参数:weight=10.5, distance=150.0 ✅ 权限校验通过 🔗 正在连接远程物流服务器... ✅ 远程物流服务已连接 📦 [RealService] 正在计算运费... 💾 [缓存] 已缓存结果:key=fee_10.5_150.0 151.25 【场景2】再次计算相同运费 📝 [日志] 第2次访问 | 方法:calculateFee | 参数:weight=10.5, distance=150.0 ✅ 权限校验通过 ⚡ [缓存命中] 直接返回缓存结果:key=fee_10.5_150.0 151.25 【场景3】查询物流轨迹 📝 [日志] 第3次访问 | 方法:trackOrder | 参数:ORD20260723001 ✅ 权限校验通过 📦 [RealService] 正在查询物流轨迹... 💾 [缓存] 已缓存结果:key=track_ORD20260723001 订单[ORD20260723001]:已发货 → 运输中 → 预计2天后到达 【场景4】再次查询相同订单 📝 [日志] 第4次访问 | 方法:trackOrder | 参数:ORD20260723001 ✅ 权限校验通过 ⚡ [缓存命中] 直接返回缓存结果:key=track_ORD20260723001 订单[ORD20260723001]:已发货 → 运输中 → 预计2天后到达 【场景5】计算不同运费 📝 [日志] 第5次访问 | 方法:calculateFee | 参数:weight=5.0, distance=80.0 ✅ 权限校验通过 📦 [RealService] 正在计算运费... 💾 [缓存] 已缓存结果:key=fee_5.0_80.0 81.25 总请求次数:5 次

5. 核心角色回顾

角色职责对应代码
Subject定义真实对象和代理的公共接口ShippingService
RealSubject真正执行业务逻辑的对象RealShippingService
Proxy持有真实对象引用,控制访问ShippingServiceProxy

6. 代理模式 vs 装饰器模式

对比项代理模式装饰器模式
意图控制访问(权限、缓存、延迟加载)增强功能(添加职责)
真实对象的创建代理自己创建(控制创建时机)由客户端创建并注入
客户端感知客户端通常不知道代理的存在客户端显式组合装饰器
嵌套能力通常一对一可层层嵌套,任意组合
典型应用Spring AOP、MyBatis Mapper、远程代理Java I/O 流、Collections.synchronizedList()

💡一句话记忆:代理是“中介”——控访问、管创建;装饰器是“加料师”——加功能、可套娃。Spring AOP 用代理控制事务、权限,Java I/O 用装饰器给流加缓冲、加密。

7. 代理模式的优缺点

优点缺点
职责分离:横切逻辑与业务逻辑解耦代理类增加,系统复杂度上升
延迟加载:避免初始化大对象动态代理(反射)有一定性能损耗
符合开闭原则:扩展无需修改原类JDK 动态代理必须基于接口

8. 六大设计原则体现

原则体现
单一职责代理负责横切控制,真实服务负责业务
开闭原则新增缓存、权限等能力通过代理扩展
里氏替换代理与真实对象实现同一接口
依赖倒置客户端依赖抽象ShippingService接口
接口隔离抽象接口方法精简
迪米特法则客户端只与代理交互,不知真实对象存在

附 代理模式 UML源码(物流服务访问控制场景)

@startuml title Java 23 种设计模式:从踩坑到精通 footer 折哥 | 智能物流与Java实战 ' 1. 全局样式配置 skinparam backgroundColor #FEFEFE skinparam shadowing false skinparam classBorderColor #333333 skinparam classFontColor #1A1A1A skinparam classFontSize 14 skinparam noteFontSize 12 skinparam noteFontColor #555555 skinparam arrowColor #555555 skinparam classBackgroundColor #F9F9F9 skinparam interface { BackgroundColor #E8F5E9 BorderColor #2E7D32 } ' 2. 抽象主题 interface ShippingService { + calculateFee(weight, distance) : double + trackOrder(orderId) : String } note right of ShippingService <b>抽象主题(物流接口)</b> -- 定义真实对象和代理对象的公共接口 客户端通过此接口访问 end note ' 3. 真实主题 class RealShippingService implements ShippingService { + calculateFee(weight, distance) : double + trackOrder(orderId) : String } note right of RealShippingService <b>真实主题(真实物流服务)</b> -- 真正执行业务逻辑 包含核心计算和查询功能 end note ' 4. 代理类 class ShippingServiceProxy implements ShippingService { - realService : RealShippingService - apiKey : String - requestCount : int + calculateFee(weight, distance) : double + trackOrder(orderId) : String - logAccess() - checkPermission() - cacheResult(key, value) } note right of ShippingServiceProxy <b>代理(物流服务代理)</b> -- 控制对真实对象的访问 可添加权限校验、日志、缓存等 延迟加载真实对象(懒加载) end note ' 5. 关系连线 ShippingService <|.. RealShippingService : 实现 ShippingService <|.. ShippingServiceProxy : 实现 ShippingServiceProxy o--> RealShippingService : 持有引用 @enduml

🧭 《Java 23 种设计模式:从踩坑到精通》快速导航

  • 开篇:系列介绍与目录
  • 正篇:代理模式 Proxy —— 你的 AOP 就是用代理实现的
  • 当前:番外 · 代理模式 × 物流服务访问控制(你在这里)
  • 创建型模式汇总
  • 结构型模式汇总
  • 行为型模式汇总

🔔 关注《Java 23 种设计模式:从踩坑到精通》,用 25 篇文章彻底吃透设计模式。
📦福利预告:全系列代码及 UML 源码将在完结时统一打包开放,点击「关注」「收藏」第一时间获取。

📌 除了设计模式,我也在深挖智能物流实战(WMS、托盘调度、机器学习落地)。欢迎点击头像,看看专栏 《出版社物流WMS智能调度实战》、《电商多平台电子面单对接实战》。技术相通,思路可鉴。

http://www.jsqmd.com/news/1303421/

相关文章:

  • 毕业论文神器!盘点2026年备受追捧的AI论文写作工具
  • 音视频干了十一年,我的技术栈窄得像根针
  • 浏览器Cookie管理神器:5分钟掌握Cookie Editor完整指南
  • 飞书AI团队协作效能评估报告(2024Q2独家数据):TOP10企业已启用「AI协作健康度」仪表盘
  • 小红书图文设计终极指南:28种专业版式让你的内容告别单调
  • 无人机视角航拍输电线金具设备数据集4481张VOC+YOLO格式
  • 当AI成为你的专属音乐制作人:SongGeneration如何重塑创作边界
  • 5分钟解密透明悬浮浏览器:如何基于Electron构建跨窗口交互新范式
  • 蓝莓品牌升级:从包装设计到市场溢价策略
  • Untrunc终极指南:专业视频修复工具深度解析与高效部署
  • 回收苹果手机哪个平台靠谱?从资质到报价全维度测评 - 甄选测评馆
  • 华硕笔记本性能解放指南:G-Helper轻量级控制工具全面解析
  • Java 23 种设计模式:从踩坑到精通 | 番外:责任链模式 —— 物流审批流程实战
  • ClickHouse版本管理深度实战:4步构建零风险升级与回滚体系
  • LLM推理模型工作机制与优化技术详解
  • 【Kimi图表解读终极指南】:20年数据可视化专家亲授5大避坑法则与3类高频误读场景破解术
  • 爱回收深度测评|手机回收哪个平台划算?四家主流渠道实测对比 - 甄选测评馆
  • llms.txt技术方案:解决大语言模型网站内容理解的架构化指南
  • 药板铝泊板缺陷检测数据集2375张VOC+YOLO格式
  • 绝区零全自动游戏助手:一键解放双手的智能自动化工具
  • 如何快速提取视频字幕:3分钟完成硬字幕转换的完整指南
  • 北京买狗必避三大深坑!这三家实体门店远离星期狗套路 - 北京同城宠物基地
  • 构建个人漫画云图书馆:Suwayomi-Server完全指南
  • 【实战指南】构建本地AI语音合成系统:开发者的高效解决方案
  • Open Generative AI:打破AI创作限制的开源解决方案
  • 字体改一改、间距调一调不算侵权?商标侵权判定标准!
  • MPV PlayKit:从技术门槛到专业级播放体验的完整解决方案
  • codex 安装脚本分析
  • 大型语言模型内部策略架构与自底向上强化学习实践
  • GoReSym逆向分析:三步破解Go二进制符号恢复难题的完整指南