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

设计模式——外观设计模式(结构型)

摘要

本文介绍了外观设计模式,它是一种结构型设计模式,通过引入一个外观类来封装复杂子系统的调用细节,对外提供简单统一的接口。文中通过生活类比、关键角色介绍、使用场景分析以及结构说明等方面对这一模式进行了全面阐述,还涉及了实现方式、适合场景、实战示例和相关思考,有助于读者深入理解外观设计模式的原理和应用。

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. ✅ 总结:外观模式实现要点

步骤

内容

把多个子系统服务拆分成独立类

建立一个 Facade

类,对外暴露统一方法

客户端只与 Facade

类交互

④(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…...

高光谱成像相机:基于高光谱成像技术的玉米种子纯度检测研究

种子纯度是衡量种子质量的核心指标之一,直接影响农作物产量与品质。传统检测方法(如形态学观察、生化分析)存在耗时长、破坏样本、依赖人工等缺陷。近年来,高光谱成像技术因其融合光谱与图像信息的优势,成为无损检测领…...