设计模式——外观设计模式(结构型)
摘要
本文介绍了外观设计模式,它是一种结构型设计模式,通过引入一个外观类来封装复杂子系统的调用细节,对外提供简单统一的接口。文中通过生活类比、关键角色介绍、使用场景分析以及结构说明等方面对这一模式进行了全面阐述,还涉及了实现方式、适合场景、实战示例和相关思考,有助于读者深入理解外观设计模式的原理和应用。
1. 外观设计模式定义
为复杂子系统提供一个统一的高层接口,使得子系统更易使用。外观模式通过引入一个“外观类(Facade)”,封装内部子系统的调用细节,对外暴露一个简单、统一的接口,隐藏系统的复杂性。
1.1. 📦 举个通俗例子(生活类比):
点外卖 = 外观模式:
你使用美团 App 点外卖,只用点菜、下单,不需要关心:
- 餐厅是否接单(子系统 A)
- 骑手怎么接单(子系统 B)
- 结算怎么走账(子系统 C)
这就是“外观类”统一封装的行为。
1.2. ✅ 关键角色:
角色 | 说明 |
Facade(外观) | 高层接口,封装子系统的复杂逻辑,对外提供简洁接口。 |
SubSystem(子系统) | 一组类或模块,完成具体业务逻辑。外观类内部会协调调用它们。 |
Client(客户端) | 只依赖外观类,屏蔽了对内部子系统的直接访问 |
1.3. ✅ 使用场景:
- 系统结构复杂,希望对外提供简化接口;
- 多个系统或模块集成,希望统一接入方式;
- 用于分层架构(如 Controller -> Service -> Facade -> Subsystem);
- 旧系统封装重构:用外观封装老接口,屏蔽调用细节。
2. 外观设计模式结构
外观模式包含如下角色:
- Facade: 外观角色
- SubSystem:子系统角色
2.1. 外观设计模式类图
2.2. 外观设计模式时序图
3. 外观设计模式实现方式
外观设计模式(Facade Pattern)的实现方式非常清晰明确:通过封装多个子系统的复杂调用逻辑,统一对外提供一个简单接口。
3.1. ✅ 实现步骤(标准实现方式)
3.1.1. 🔹 步骤 1:定义多个子系统(SubSystem)类
@Service
public class RiskScoreService {public int getRiskScore(String userId) {System.out.println("计算用户风险分...");return 75;}
}@Service
public class BlacklistService {public boolean isBlacklisted(String userId) {System.out.println("检查黑名单...");return false;}
}@Service
public class CreditService {public int getCreditLimit(String userId) {System.out.println("获取信用额度...");return 5000;}
}
3.1.2. 🔹 步骤 2:定义外观类(Facade)
@Servcie
public class RiskFacade {@Autowiredprivate final RiskScoreService riskScoreService;@Autowiredprivate final BlacklistService blacklistService;@Autowiredprivate final CreditService creditService;public void assessUserRisk(String userId) {System.out.println("开始用户风控评估...");if (blacklistService.isBlacklisted(userId)) {System.out.println("用户被拉黑,拒绝服务!");return;}int score = riskScoreService.getRiskScore(userId);int credit = creditService.getCreditLimit(userId);System.out.println("风控评估完成,风险分:" + score + ",信用额度:" + credit);}
}
3.1.3. 🔹 步骤 3:客户端只使用 Facade,不关心子系统
public class Client {public static void main(String[] args) {RiskFacade facade = new RiskFacade();facade.assessUserRisk("user123");}
}
3.2. ✅ 在 Spring 项目中的实现方式
如果项目使用 Spring 框架,我们通常会将子系统类标记为 @Service
,将外观类作为一个统一入口暴露:
3.2.1. 🔹 子系统类(Spring Bean)
@Service
public class RiskScoreService { ... }@Service
public class BlacklistService { ... }@Service
public class CreditService { ... }
3.2.2. 🔹 外观类作为统一接口
@Servcie
public class RiskFacade {@Autowiredprivate RiskScoreService riskScoreService;@Autowiredprivate BlacklistService blacklistService;@Autowiredprivate CreditService creditService;public void assess(String userId) {// 同上,统一调用子服务}
}
3.2.3. 🔹 Controller 层只依赖外观类
@RestController
@RequestMapping("/risk")
public class RiskController {@Autowiredprivate RiskFacade riskFacade;@GetMapping("/assess")public String assess(@RequestParam String userId) {riskFacade.assess(userId);return "评估完成";}
}
3.3. ✅ 总结:外观模式实现要点
步骤 | 内容 |
① | 把多个子系统服务拆分成独立类 |
② | 建立一个 类,对外暴露统一方法 |
③ | 客户端只与 类交互 |
④(Spring) | 把子系统类交给 Spring 管理,外观类通过注入协调调用 |
4. 外观设计模式适合场景
4.1. ✅ 适合使用外观设计模式的场景
场景 | 说明 |
系统结构复杂 | 系统由多个子系统组成,接口调用复杂,客户端需要简化调用流程。 |
统一访问入口 | 需要为多个子系统提供一个统一的接口,客户端只需调用这个接口。 |
分层架构设计 | 在分层架构中,用外观层屏蔽底层子系统的实现细节,降低耦合度。 |
系统重构与迁移 | 需要将旧系统接口封装,兼容老旧代码,逐步迁移到新系统。 |
多子系统协调 | 需要协调多个子系统的调用顺序或组合调用逻辑,外观类负责协调。 |
4.2. ❌ 不适合使用外观设计模式的场景
场景 | 原因 |
系统简单 | 业务流程简单,接口调用不复杂,使用外观反而增加额外层次和复杂度。 |
单一功能模块 | 只涉及一个功能模块,没有必要额外封装统一接口。 |
频繁变动接口 | 子系统接口频繁改变,外观层也需频繁修改,维护成本高。 |
业务高度耦合 | 业务流程需要客户端灵活控制子系统内部调用,外观隐藏细节不合适。 |
5. 外观设计模式实战示例
下面是一个金融风控场景下的外观设计模式实战示例,演示如何用Spring管理所有对象,并且使用注解方式注入,避免构造函数注入,方便集成和维护。
5.1. 项目背景
风控系统需要对用户进行风险评估,涉及多个子系统服务:
- 黑名单查询服务(BlacklistService)
- 风险分数计算服务(RiskScoreService)
- 信用额度服务(CreditService)
通过外观模式(RiskFacade)统一暴露给业务调用层,隐藏各子系统复杂调用。
5.2. 子系统服务类
@Service
public class BlacklistService {public boolean isBlacklisted(String userId) {System.out.println("检查用户是否在黑名单中...");// 模拟黑名单检查逻辑return "blacklistedUser".equals(userId);}
}@Service
public class RiskScoreService {public int calculateRiskScore(String userId) {System.out.println("计算用户风险分数...");// 模拟风险分数计算return 80;}
}@Service
public class CreditService {public int getCreditLimit(String userId) {System.out.println("获取用户信用额度...");// 模拟信用额度查询return 10000;}
}
5.3. 外观类
@Component
public class RiskFacade {@Autowiredprivate BlacklistService blacklistService;@Autowiredprivate RiskScoreService riskScoreService;@Autowiredprivate CreditService creditService;public void assessUserRisk(String userId) {System.out.println("=== 开始风控评估 ===");if (blacklistService.isBlacklisted(userId)) {System.out.println("用户 " + userId + " 在黑名单中,拒绝服务!");return;}int riskScore = riskScoreService.calculateRiskScore(userId);int creditLimit = creditService.getCreditLimit(userId);System.out.println("用户 " + userId + " 风险分数: " + riskScore);System.out.println("用户 " + userId + " 信用额度: " + creditLimit);System.out.println("=== 评估结束 ===");}
}
5.4. Controller 层示例
@RestController
@RequestMapping("/risk")
public class RiskController {@Autowiredprivate RiskFacade riskFacade;@GetMapping("/assess")public String assessRisk(@RequestParam String userId) {riskFacade.assessUserRisk(userId);return "风控评估完成";}
}
说明
- 所有类都由Spring管理,使用
@Service
和@Component
注解。 - 外观类
RiskFacade
注入所有子系统服务,作为统一调用入口。 - 业务层(Controller)只调用外观类接口,避免直接依赖多个子系统。
- 避免构造函数注入,使用
@Autowired
注解实现自动注入,符合你的要求。
6. 外观设计模式思考
6.1. 门面设计设计模式和DDD中Facade设计区别
6.1.1. 门面设计模式(Facade Pattern)
- 定义:Facade 是一种结构型设计模式,用于为复杂的子系统提供一个简化的统一接口。它屏蔽了系统的复杂性,客户端通过门面类与子系统交互,而无需直接了解子系统的实现细节。
- 关注点:简化接口,降低客户端与子系统之间的耦合。
- 典型用途:
-
- 为一个复杂系统提供统一的入口。
- 隐藏子系统的内部复杂逻辑。
- 提高客户端调用的便利性。
6.1.2. DDD 中的 Facade
- 定义:在领域驱动设计中,Facade 是一个用于协调多个领域对象或领域服务的接口或类。它通常用于应用层,作为应用服务的一部分,负责将客户端的请求转化为对领域层的调用。
- 关注点:隔离应用层与领域层,简化应用层与外部系统(如 UI、接口调用等)的交互。
- 典型用途:
-
- 在应用层对外暴露接口。
- 封装复杂的领域操作,协调多个领域对象和领域服务。
- 承载用例(Use Case)的实现逻辑。
6.1.3. 核心区别
维度 | 门面设计模式(Facade Pattern) | DDD 中的 Facade |
目的 | 为复杂子系统提供一个统一、简化的接口,屏蔽系统内部实现细节。 | 为外部系统(如 UI 层、API 层)提供对领域层的调用接口。 |
适用范围 | 用于封装技术组件(子系统、模块、服务)。 | 用于封装领域逻辑,暴露领域行为。 |
位置 | 通常在技术实现层,用于协调多个技术模块。 | 通常在应用层,调用领域层服务或聚合根。 |
关注点 | 简化客户端调用,隐藏子系统复杂性。 | 承载用例逻辑,协调领域对象和服务,实现业务需求。 |
是否直接操作领域 | 通常不直接操作领域对象,只封装系统内部的模块调用。 | 直接操作领域对象、聚合根、领域服务等。 |
6.2. 门面设计模式(Facade Pattern)的设计思想与 Application 层 的职责相似
6.2.1. Application层与门面模式(Facade Pattern)
Application 层(DDD 中的角色)
- 主要职责:
-
- 提供用例逻辑(Use Case)服务。
- 负责协调领域层(Domain Layer)的多个领域对象、领域服务和聚合根。
- 为外部系统(如 API 层、UI 层等)提供统一的调用接口。
- 不包含业务逻辑,业务逻辑属于领域层,它仅负责调度领域逻辑。
- 核心思想:
-
- 简化外部调用(UI 层或 API 层)对复杂领域逻辑的访问。
- 将应用层与领域层隔离,保证领域层专注于业务规则,而应用层处理系统操作的组合与流程。
门面模式(Facade Pattern)
- 主要职责:
-
- 为子系统提供一个统一接口,屏蔽系统内部的复杂性。
- 将多个子系统或模块的调用逻辑封装在一个类中,外部调用方无需了解子系统的细节。
- 简化客户端调用,降低外部代码与子系统的耦合度。
- 核心思想:
-
- 提供一个简化的、高层次的接口来调用内部复杂的逻辑或子系统。
6.2.2. Application 层和门面模式的相似性
维度 | Application 层 | 门面模式(Facade Pattern) |
主要职责 | 调用领域层的对象和服务,为外部系统提供统一的调用接口。 | 调用子系统的服务,为客户端提供简化的调用入口。 |
目标 | 简化外部系统对复杂领域逻辑的调用,协调领域服务和对象。 | 隐藏子系统的复杂性,为客户端提供简化的接口。 |
隐藏复杂性 | 隐藏领域层内部对象之间的交互细节。 | 隐藏子系统之间的交互细节。 |
调用方 | 外部系统(UI 层、API 层等)。 | 客户端或其他模块。 |
实现的粒度 | 以业务用例为单位,封装一个完整的应用逻辑。 | 以技术组件为单位,封装多个模块或服务的调用逻辑。 |
结论:两者都承担了“简化复杂性、统一接口”的职责,但 Application 层更专注于领域逻辑的编排和业务用例,而门面模式更关注技术子系统的整合和封装。
6.3. 项目提供RPC 服务接口设计是不是属于的门面设计模式?
是的,可以认为属于门面设计模式的应用。门面模式(Facade Pattern)定义:为子系统中的一组复杂接口提供一个统一的高层接口,使子系统更易使用。
Spring 中 RPC 服务接口的特点(比如基于 Dubbo、gRPC、Spring Cloud):
特性 | 说明 |
📦 对外暴露服务接口 | Controller 不再是主角,RPC 接口才是系统“对外的门面” |
⚙️ 封装多个底层业务服务(Service、DAO、组件等) | 对外提供统一调用入口 |
🔐 对调用者隐藏实现细节 | 调用方只知道接口,内部逻辑对其不可见 |
🔌 提供远程访问能力 | 一般用于微服务、分布式系统间通信 |
这与门面模式的核心思想高度一致:对复杂子系统提供统一、简洁、高层次的访问接口。示例场景:
假设你有一个贷款审批系统,下游调用方只需调用如下 RPC 接口:
public interface LoanApprovalRpcService {ApprovalResult approveLoan(LoanApplicationDTO application);
}
而这个接口内部其实会:
- 校验申请数据
- 调用风控系统
- 查询信用评分
- 写入审批日志
- 通知第三方平台
这时你暴露的这个 RPC 接口就非常标准地扮演了“门面”角色:
- 对外隐藏了所有复杂的逻辑
- 调用者只需要关心一个方法:
approveLoan(...)
博文参考
- 4. 外观模式 — Graphic Design Patterns
- 外观设计模式(门面模式)
相关文章:

设计模式——外观设计模式(结构型)
摘要 本文介绍了外观设计模式,它是一种结构型设计模式,通过引入一个外观类来封装复杂子系统的调用细节,对外提供简单统一的接口。文中通过生活类比、关键角色介绍、使用场景分析以及结构说明等方面对这一模式进行了全面阐述,还涉…...

Linux `vi/vim` 编辑器深度解析与高阶应用指南
Linux `vi/vim` 编辑器深度解析与高阶应用指南 一、核心功能解析1. 模式系统2. 与主流编辑器对比二、核心操作体系1. 高效导航命令2. 文本操作矩阵三、高阶配置体系1. .vimrc 配置示例2. 插件管理系统四、企业级开发实践1. 代码编辑技巧2. 宏录制与批量处理五、可视化与多窗口1…...

ES中must与filter的区别
在 Elasticsearch 的布尔查询(bool query)中,must 和 filter 是两个核心子句,它们的核心区别在于 是否影响相关性评分,这直接决定了它们在查询性能、使用场景和结果排序上的差异。以下是详细对比: 一、核心…...

qt之开发大恒usb3.0相机三
上一篇大恒相机的开发 是基于Qt Creator msvc工具链编译的,大恒相机msvc使用的的lib库是c版的。如果想要使用mingw工具链开发大恒相机,那么找连接对相应的lib库。mingw对应的库是c的。 配置如下: 图像获取核心代码如下 void __stdcall Wid…...

Transformer架构详解:从Attention到ChatGPT
Transformer架构详解:从Attention到ChatGPT 系统化学习人工智能网站(收藏):https://www.captainbed.cn/flu 文章目录 Transformer架构详解:从Attention到ChatGPT摘要引言一、Attention机制:Transformer的…...

数据中台(大数据平台)之数据安全管理
数据安全管理是结合大数据技术和行业特性,数据中台产品应具备数据分类分级、敏感数据智能识别的功能,并结合敏感数据管理、数据脱敏、数据加密等安全管控方式,保障数据安全可用。 1.安全分级分类:数据分级分类是一种将不同数据按…...

github双重验证密码忘记或者获取不了了怎么办
背景 近期由于换了新手机,之前配置好的Authenticator这个App无法使用,导致获取不到二次验证的Authenticator code,登陆不上GitHub,不知道有没有人和我遇到同样的问题? 当我们配置2FA双重验证后,每次登陆gi…...

告别复杂操作!电脑极简风格计时使用
无论是工作、学习还是日常生活,这款小巧实用的计时工具都能成为你掌控时间的好帮手。特别适合需要频繁切换正计时、倒计时和查看当前时间的场景。界面简洁,操作便捷,助你高效管理每一刻。 这是一款免安装的工具,下载后可直接打开…...
stm32cube ide如何将工具链替换成arm-none-eabi-gcc
在 STM32Cube IDE 中替换工具链为GNU Arm Embedded Toolchain (arm-none-eabi-gcc),可按以下步骤操作: 1. 检查是否已安装工具链 首先确认系统中是否已安装 arm-none-eabi-gcc: Windows:检查环境变量 PATH 中是否包含工具链路径…...

[STM32问题解决(2)]STM32通过串口与PC通信,打开串口助手后无法在打开状态下下载程序和复位STM32
问题回顾 最近学习STM32单片机,经常使用STM32通过USART1串口与PC的串口助手进行通信。为了简单便捷,通常在打开串口的状态下下载程序。这样子下载程序后,STM32发出的信号,PC马上可以收到。 但是,突然出现了一个问题&a…...
RabbitMQ 与其他 MQ 的对比分析:Kafka/RocketMQ 选型指南(二)
四、三者性能大比拼 4.1 吞吐量 吞吐量是衡量消息队列处理能力的重要指标,它反映了在单位时间内消息队列能够处理的消息数量。在这方面,Kafka 表现最为出色,其独特的设计使其能够轻松处理每秒数百万条消息 。Kafka 采用分布式架构和分区机制…...

OpenHarmony定制系统组合按键(一)
一、开发环境 系统版本:OpenHarmony 4.0.10.13 设备平台:rk3568 SDK版本:fullSDK 4.0.10.13 DevEco Studio版本:4.1.0.400 二、需求背景 定制OpenHarmony 系统组合按键功能,例如仿Android Power VOL_Up组合键实现截…...
ORDER BY子句在一个 SQL 查询中只能出现一次
order by A.create_time,A.update_time desc和 order by A.create_time desc,A.update_time desc有区别吗? 关键区别 第一个排序中 create_time 是升序(默认是ASC),第二个是降序(DESC) 只有在 DESC 关键字紧跟在列名后面时,该列才会按降序排…...

Spring Boot 3 整合 MQ 构建聊天消息存储系统
引子 在构建实时聊天服务时,我们既要保证消息的即时传递,又需要对消息进行持久化存储以便查询历史记录。然而,直接同步写入数据库在高并发场景下容易成为性能瓶颈,影响消息的实时性。秉承"没有什么问题是加一层解决不了的&q…...
DeepSeek实战:打造智能数据分析与可视化系统
DeepSeek实战:打造智能数据分析与可视化系统 1. 数据智能时代:DeepSeek数据分析系统入门 在数据驱动的决策时代,智能数据分析系统正成为企业核心竞争力。本节将使用DeepSeek构建一个从数据清洗到可视化分析的全流程智能系统。 1.1 系统核心功能架构 class DataAnalysisS…...

非线性声学计算与强化学习融合框架:突破复杂环境人机交互的新技术
随着人工智能的快速发展,尤其是在深度学习和强化学习领域,声学计算和人机交互进入前所未有的扩展和创新阶段。尽管传统声学方法取得了显著成功,但这些线性或准线性方法在实际环境中往往存在关键的不足,尤其在动态、复杂或混响环境…...

C++ - STL #什么是STL #STL的版本 #闭源开源 #STL的六大组件
文章目录 前言 一、什么是STL 二、STL的版本 1、原始版本 2、P.J.版本 3、RW版本 4、SGI版本 三、闭源、开源 四、STL的六大组件 总结 前言 路漫漫其修远兮,吾将上下而求索; 一、什么是STL STL(standard template libaray 标准模板库)&#…...

Flutter - 原生交互 - 相机Camera - 01
环境 Flutter 3.29 macOS Sequoia 15.4.1 Xcode 16.3 集成 Flutter提供了camera插件来拍照和录视频,它提供了一系列可用的相机,并使用特定的相机展示相机预览、拍照、录视频。 添加依赖 camera: 提供使用设备相机模块的工具path_provider: 寻找存储图…...

湖北理元理律师事务所:个人债务管理的温度与精度
湖北理元理律师事务所:个人债务管理的温度与精度 面对信用卡、网贷、医疗债等多重债务压力,普通人常陷入“拆东墙补西墙”的恶性循环。湖北理元理律师事务所通过计划集团公司服务平台,推出“有温度的债务优化计划”,其人性化设计…...

Compose原理 - 整体架构与主流程
一、整体架构 在官方文档中(Jetpack Compose 架构层 | Android Developers),对Compose的分层有所阐述: 其中 Runtime:提供Compose的基础运行能力,包括State、Side-effects、CompositionLocal、Compositio…...
从0开始学vue:实现一个简单页面
Vue.js 是一个渐进式JavaScript框架,用于构建用户界面。下面我将带你从零开始学习Vue.js并创建一个简单的可运行页面。 1. 准备工作 首先,你需要了解几种学习Vue.js的方式: 方式一:使用CDN引入(最简单的方式&#x…...
在机器视觉测量和机器视觉定位中,棋盘格标定如何影响精度
棋盘格标定是机器视觉(尤其是基于相机的系统)中进行相机内参(焦距、主点、畸变系数)和外参(相机相对于世界坐标系的位置和姿态)标定的经典且广泛应用的方法。它的质量直接、显著且多方面地影响最终的视觉测量和定位精度。 以下是棋盘格标定如何影响精度的详细分析: 标定…...

CppCon 2014 学习: C++ Test-driven Development
“Elephant in the Room”这个比喻常用来形容那些大家都知道但没人愿意讨论的重大问题。 这段内容讲的是软件质量管理的经典做法和潜在的问题: 经典做法:开发完成后才进行人工测试(manual testing after creation)。隐喻“Cape o…...

RAGflow详解及实战指南
目录 前言 一、RAGflow核心技术解析 1. 技术原理:检索与生成的协同进化 2. 架构设计:分层模块化与高扩展性 3. 核心优势:精准、高效、安全 二、RAGflow实战应用场景 1. 企业知识库搭建 2. 智能客服系统 3. 投资分析报告生成 4. 制造…...
JWT 不对外,Session ID 对外:构建安全可控的微服务认证架构
以下是一篇围绕“JWT不对外,Session ID对外”的专业架构设计文章,适用于技术团队评审、技术博客发布或系统设计文档引用: JWT 不对外,Session ID 对外:构建安全可控的微服务认证架构 在构建分布式微服务系统时&#x…...

[Godot] 如何导出安卓 APK 并在手机上调试
在之前的文章中,我们已经详细介绍了如何配置 Godot 的安卓应用开发环境,包括安装 Android SDK、配置 Java 环境、设置 Godot 的 Android 导出模板等。本篇文章将进一步讲解如何将 Godot 项目导出为安卓 APK 文件,并实现在手机上进行调试运行。…...
React 路由管理与动态路由配置实战
React 路由管理与动态路由配置实战 前言 在现代单页应用(SPA)开发中,路由管理已经成为前端架构的核心部分。随着React应用规模的扩大,静态路由配置往往难以满足复杂业务场景的需求,尤其是当应用需要处理权限控制、动态菜单和按需加载等高级…...
ZYNQ sdk lwip配置UDP组播收发数据
🚀 一、颠覆认知:组播 vs 单播 vs 广播 通信方式目标设备网络负载典型应用场景单播1对1O(n)SSH远程登录广播1对全网O(1)ARP地址解析组播1对N组O(1)视频会议/物联网群控创新价值:在智能工厂中,ZYNQ通过组播同时控制100台AGV小车,比传统单播方案降低92%网络流量! 🔧 二、…...
11.21 LangGraph多轮对话系统实战:三步构建高效信息整理引擎,效率提升300%!
关键词:LangGraph 工作流设计, 信息整理助理, 多轮对话系统, 状态管理, 条件分支控制 信息整理助理工作流设计 信息整理助理需要完成 多源数据收集 → 信息分类 → 深度分析 → 结构化输出 的完整流程。通过 LangGraph 的图结构工作流,可实现复杂逻辑的模块化编排: #mermai…...
高光谱成像相机:基于高光谱成像技术的玉米种子纯度检测研究
种子纯度是衡量种子质量的核心指标之一,直接影响农作物产量与品质。传统检测方法(如形态学观察、生化分析)存在耗时长、破坏样本、依赖人工等缺陷。近年来,高光谱成像技术因其融合光谱与图像信息的优势,成为无损检测领…...