news 2026/4/25 5:37:22

设计模式学习(12) 23-10 外观模式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
设计模式学习(12) 23-10 外观模式

文章目录

  • 0.个人感悟
  • 1. 概念
  • 2. 适配场景
    • 2.1 适合的场景
    • 2.2 常见场景举例
  • 3. 实现方法
    • 3.1 实现思路
    • 3.2 UML类图
    • 3.3 代码示例
  • 4. 优缺点
    • 4.1 优点
    • 4.2 缺点
  • 5. 源码分析(MyBatis Configuration为例)

0.个人感悟

  • 外观模式旨在承上启下,对客户端提供一个统一接口,只定义需要关注的操作,对下统筹各个子系统的操作
  • 外观模式很能体现出解耦的一个手段:分层
  • 外观模式有利于理解迪米特法则(最小知道原则)
  • 外观(门面)可以类比web编程中controller,都是对外提供统一的接口,对内整合自己的业务

1. 概念

英文定义(《设计模式:可复用面向对象软件的基础》)

Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.

中文翻译

为子系统中的一组接口提供一个统一的接口。外观模式定义了一个高层接口,使得子系统更容易使用。

理解

  • 外观模式是一种结构型设计模式,它为复杂的子系统提供一个简单易用的接口
  • 通过引入一个外观类(Facade),将客户端与子系统的复杂交互封装起来
  • 外观模式不改变子系统功能,只是提供了一个更易于访问的入口点
  • 实现了客户端与子系统之间的解耦,使子系统更容易维护和扩展
  • 外观模式符合迪米特法则(最少知识原则),客户端只需要与外观类交互

2. 适配场景

2.1 适合的场景

  1. 需要简化复杂子系统接口时,为子系统提供一个统一的入口
  2. 客户端与多个子系统之间存在大量依赖关系,希望降低耦合度
  3. 需要将子系统分层,为每一层提供统一的接口
  4. 系统需要逐步重构,可以先引入外观模式,然后逐步迁移到新系统

2.2 常见场景举例

  • 电脑启动过程:用户只需按下电源键,无需了解BIOS、CPU、内存等组件的复杂交互
  • 数据库连接:JDBC驱动管理器封装了不同数据库的连接细节
  • Web服务接口:REST API网关整合多个微服务的调用
  • 日志框架:SLF4J作为Logback、Log4j等日志实现的外观
  • 支付系统:支付网关整合支付宝、微信支付、银联等不同支付渠道

3. 实现方法

3.1 实现思路

  1. 识别复杂子系统:分析系统中的各个组件和它们之间的依赖关系
  2. 定义外观接口:确定需要为客户端提供的简化操作
  3. 实现外观类:创建外观类,封装子系统的复杂调用逻辑
  4. 客户端通过外观类访问:客户端只与外观类交互,不直接调用子系统
  5. 可选:抽象外观:如果需要支持多个子系统变体,可以引入抽象外观类

3.2 UML类图

角色说明

  • Facade(外观):为子系统提供一个统一的接口,知道哪些子系统负责处理请求
  • Subsystem Classes(子系统类):实现子系统的功能,处理外观对象指派的任务
  • Client(客户端):通过外观接口与子系统交互,不需要了解子系统的内部细节

3.3 代码示例

背景:电脑的启动重启过程,涉及到很多子系统的操作,但是机箱其实只提供了开机关机重启按钮,这就是很典型的外观模式。简化掉bios等流程,代码如下:
各个子系统,简化为CPU 内存 硬盘:

// CPU子系统publicclassCPU{/** * @description 冻结 * @author bigHao * @date 2026/1/12 **/publicvoidfreeze(){System.out.println("CPU冻结当前任务");}/** * @param position 位置 * @description 跳转 * @author bigHao * @date 2026/1/12 **/publicvoidjump(longposition){System.out.println("CPU跳转到内存位置: "+position);}/** * @description 执行 * @author bigHao * @date 2026/1/12 **/publicvoidexecute(){System.out.println("CPU开始执行指令");}}// 内存子系统publicclassMemory{/** * @param position 位置 * @param data 字节数据 * @description // TODO * @author bigHao * @date 2026/1/12 **/publicvoidload(longposition,byte[]data){System.out.println("内存加载数据到位置: "+position);}/** * @description 释放 * @author bigHao * @date 2026/1/12 **/publicvoidshutdown(){System.out.println("内存释放");}}// 硬盘子系统publicclassHardDrive{/** * @param lba 扇区 * @param size 大小 * @return byte[] * @description 读取数据 * @author bigHao * @date 2026/1/12 **/publicbyte[]read(longlba,intsize){System.out.println("硬盘读取扇区 "+lba+",大小: "+size+" bytes");returnnewbyte[size];}/** * @description 释放 * @author bigHao * @date 2026/1/12 **/publicvoidshutdown(){System.out.println("硬盘读释放");}}

外观类,类似于机箱,这里也可以先定义接口,再提供实现

publicclassComputerFacade{// 启动内存地址常量privatestaticfinallongBOOT_ADDRESS=0x7C00;privatestaticfinallongBOOT_SECTOR=0;privatestaticfinalintSECTOR_SIZE=512;privateCPUcpu;privateMemorymemory;privateHardDrivehardDrive;publicComputerFacade(){cpu=newCPU();memory=newMemory();hardDrive=newHardDrive();}/** * @description 启动 * @author bigHao * @date 2026/1/12 **/publicvoidstart(){System.out.println("=== 开始启动计算机 ===\n");// 硬盘加载数据byte[]bootSector=hardDrive.read(BOOT_SECTOR,SECTOR_SIZE);// 加载到内存memory.load(BOOT_ADDRESS,bootSector);// cpu运行cpu.freeze();cpu.jump(BOOT_ADDRESS);cpu.execute();}/** * @description 关机 * @author bigHao * @date 2026/1/12 **/publicvoidshutdown(){System.out.println("=== 开始关闭计算机 ===\n");// cpu停止cpu.freeze();// 内存停止memory.shutdown();// 硬盘停止hardDrive.shutdown();}/** * @description 重启 * @author bigHao * @date 2026/1/12 **/publicvoidrestart(){System.out.println("=== 开始重启计算机 ===\n");start();shutdown();}}

测试:

publicclassClient{staticvoidmain(){// 只用与门面交互ComputerFacadefacade=newComputerFacade();facade.start();facade.shutdown();facade.restart();}}

输出

=== 开始启动计算机 === 硬盘读取扇区 0,大小: 512 bytes 内存加载数据到位置: 31744 CPU冻结当前任务 CPU跳转到内存位置: 31744 CPU开始执行指令 === 开始关闭计算机 === CPU冻结当前任务 内存释放 硬盘读释放 === 开始重启计算机 === === 开始启动计算机 === 硬盘读取扇区 0,大小: 512 bytes 内存加载数据到位置: 31744 CPU冻结当前任务 CPU跳转到内存位置: 31744 CPU开始执行指令 === 开始关闭计算机 === CPU冻结当前任务 内存释放 硬盘读释放

4. 优缺点

4.1 优点

符合高内聚低耦合原则

  • 降低耦合度:将客户端与复杂的子系统解耦,客户端只依赖外观类
  • 提高内聚性:外观类将相关的子系统操作封装在一起
    提高复用性
  • 外观类可以被多个客户端复用,避免重复编写复杂的子系统调用代码
    增强可维护性
  • 子系统内部变化不会影响客户端,只需要修改外观类
  • 便于分层和模块化管理
    提高可读性
  • 简化了客户端代码,使其更加清晰易懂
  • 提供了清晰的系统边界和接口
    符合开闭原则
  • 可以扩展外观类来添加新功能,而不需要修改现有代码

4.2 缺点

可能违反单一职责原则

  • 如果外观类过于庞大,可能承担了太多职责
    性能开销
  • 额外的调用层可能带来轻微的性能损失
    灵活性受限
  • 对于需要访问子系统特定功能的客户端,可能需要绕过外观类

5. 源码分析(MyBatis Configuration为例)

MyBatis中的Configuration类是外观模式的典型应用,它封装了MyBatis框架的复杂配置和初始化过程。
MyBatis Configuration类结构

// Configuration类充当了MyBatis的外观类publicclassConfiguration{// 存储各种配置信息protectedEnvironmentenvironment;protectedTypeAliasRegistrytypeAliasRegistry;protectedTypeHandlerRegistrytypeHandlerRegistry;protectedMapperRegistrymapperRegistry;protectedMap<String,MappedStatement>mappedStatements;protectedMap<String,Cache>caches;// 各种配置方法 - 对外提供简单接口publicvoidaddMappers(StringpackageName){mapperRegistry.addMappers(packageName);}public<T>voidaddMapper(Class<T>type){mapperRegistry.addMapper(type);}publicvoidaddMappedStatement(MappedStatementms){mappedStatements.put(ms.getId(),ms);}publicMappedStatementgetMappedStatement(Stringid){returnmappedStatements.get(id);}// 类型处理器相关方法publicvoidregisterTypeHandler(TypeHandler<?>typeHandler){typeHandlerRegistry.register(typeHandler);}publicTypeHandler<?>getTypeHandler(Class<?>javaType){returntypeHandlerRegistry.getTypeHandler(javaType);}}

外观模式分析:
外观角色Configuration

  • 封装了MyBatis的所有配置信息
  • 提供了统一的方法来访问各个组件
    子系统角色
  • XMLConfigBuilder:解析XML配置文件
  • MapperRegistry:管理Mapper接口
  • TypeHandlerRegistry:管理类型处理器
  • MappedStatement:管理SQL映射语句
    客户端SqlSessionFactoryBuilderDefaultSqlSessionFactory

设计优势

  1. 简化使用:用户只需要配置Configuration,不需要了解内部复杂的解析和初始化过程
  2. 解耦SqlSessionFactory只依赖Configuration外观类,不直接依赖各个子系统
  3. 可维护性:配置逻辑的变化被封装在Configuration和相关子系统中
  4. 灵活性:可以通过扩展Configuration来支持不同的配置方式
    这种多层外观设计使得MyBatis具有很好的层次结构和模块化,每个层次都封装了特定的复杂性,为上层提供简单的接口。

参考:

  • 韩顺平 Java设计模式
  • 张维鹏 Java设计模式之结构型:外观模式
  • kosamino 设计模式之外观模式(Facade)详解及代码示例
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 20:50:32

图解说明硬件I2C起始与停止条件实现原理

深入理解硬件I2C的起始与停止&#xff1a;不只是拉高拉低在嵌入式开发中&#xff0c;你有没有遇到过这样的场景&#xff1f;调试一个温湿度传感器&#xff0c;代码写得严丝合缝&#xff0c;地址也核对了八百遍&#xff0c;可就是读不到数据。用逻辑分析仪一抓——SDA线卡在低电…

作者头像 李华
网站建设 2026/4/17 8:16:12

传统vs深度学习:骨骼检测方法对比,云端快速验证

传统vs深度学习&#xff1a;骨骼检测方法对比&#xff0c;云端快速验证 引言&#xff1a;为什么需要骨骼检测技术&#xff1f; 骨骼检测&#xff08;又称人体关键点检测&#xff09;是计算机视觉领域的重要技术&#xff0c;它能从图像或视频中定位人体的关节位置&#xff08;…

作者头像 李华
网站建设 2026/4/20 18:12:36

HY-MT1.5-1.8B vs 商业翻译API:实测对比报告

HY-MT1.5-1.8B vs 商业翻译API&#xff1a;实测对比报告 1. 引言 在全球化加速的背景下&#xff0c;高质量、低延迟的机器翻译能力已成为智能应用的核心基础设施。无论是跨境电商、跨国协作&#xff0c;还是内容本地化与实时通信&#xff0c;用户对翻译服务的需求正从“能用”…

作者头像 李华
网站建设 2026/4/20 18:24:06

AI人脸隐私卫士在社交媒体的应用:用户上传预处理实战

AI人脸隐私卫士在社交媒体的应用&#xff1a;用户上传预处理实战 1. 引言&#xff1a;社交媒体时代的隐私挑战与自动化应对 随着社交媒体的普及&#xff0c;用户每天上传数以亿计的照片和视频。然而&#xff0c;这些内容中往往包含大量未授权的人脸信息——无论是街拍、聚会合…

作者头像 李华
网站建设 2026/4/21 17:42:36

HY-MT1.5-1.8B性能优化:让翻译速度提升3倍的技巧

HY-MT1.5-1.8B性能优化&#xff1a;让翻译速度提升3倍的技巧 在实时翻译、边缘计算和多语言交互日益普及的今天&#xff0c;模型推理效率直接决定了用户体验与部署成本。腾讯开源的混元翻译模型HY-MT1.5-1.8B凭借其“小体积、高质量”的特性&#xff0c;成为轻量级翻译场景中的…

作者头像 李华
网站建设 2026/4/24 13:14:51

利用AXI DMA实现千兆以太网数据直传

打通高速数据动脉&#xff1a;AXI DMA如何让千兆以太网“零拷贝”飞起来你有没有遇到过这样的场景&#xff1f;FPGA系统接上千兆网口&#xff0c;满心期待地抓取视频流或传感器数据&#xff0c;结果刚到几百兆速率就开始丢包。调试发现CPU占用率飙到90%以上&#xff0c;几乎被中…

作者头像 李华