[设计模式/Java] 设计模式之门面模式(外观模式)【20】
概述 : 门面模式 := 外观模式 := Facade Pattern
产生背景
- 软件开发过程中,我们经常会遇到复杂系统,其中包含多个子系统和接口。在这种情况下,为了简化客户端的调用过程,提高代码的可维护性和可读性,我们可以使用门面模式。
模式定义
- 门面模式(
Facade Pattern)也叫做外观模式,是一种结构型设计模式。
它提供一个统一的接口,封装了一个或多个子系统的复杂功能,并向客户端提供一个简单的调用方式。
通过引入门面,客户端无需直接与子系统交互,而只需要通过门面来与子系统进行通信。
模式的组成
- 门面(
Facade):门面角色是门面模式的核心,它封装了系统内部复杂子系统的接口,为客户端提供一个简单的高层接口。门面角色知道哪些子系统负责处理请求,并将请求转发给相应的子系统进行处理。 - 子系统(
Subsystem):子系统角色是实际执行系统功能的组件。每个子系统都有自己的职责和行为,通过门面角色对外提供服务。 - 客户端(
Client):客户端角色通过调用门面角色提供的高层接口来使用系统功能,而无需直接与子系统交互。
在门面模式中,门面角色充当了客户端和子系统之间的中介者,隐藏了子系统的复杂性,简化了客户端的调用过程。
客户端只需要与门面角色进行交互,而不需要了解和处理子系统的具体细节。
- 【特别注意】
- 门面对象只是提供一个访问子系统的一个路径而已,它不应该也不能参与具体的业务逻辑;
- 否则,就会产生一个倒依赖的问题:子系统必须依赖门面才能被访问,这是设计上一个严重错误,不仅会违反了单一职责原则,同时也破坏了系统的封装性。
适用场景
- 当一个系统有很多复杂的子系统时,可以使用门面模式将其封装起来,隐藏内部复杂性,简化客户端的调用。
- 当需要将客户端与复杂的子系统做解耦,降低系统之间的依赖时,可以使用门面模式。
模式特点
优点
- 简化客户端的调用过程,隐藏了子系统的复杂性,提供了一个统一的接口,客户端无需了解子系统的具体实现。
- 减少系统的相互依赖,解耦了客户端与子系统之间的依赖关系。
- 提高了代码的可维护性和可读性。
缺点
- 门面模式可能会导致门面类变得庞大,承担过多的责任。
- 如果需要修改子系统的功能,可能需要修改门面类。
门面模式的优化
在实际应用中,我们可以对门面模式进行一些优化和扩展。以下是几个常见的优化实现方式:
子系统解耦
- 门面类可以通过委托来调用子系统的功能,而不是直接依赖于具体的子系统。
这样可以使得子系统能够独立演化,不受门面类的影响。
// 门面类
class Facade {
private SubSystemInterface subSystemA;
private SubSystemInterface subSystemB;
public Facade() {
subSystemA = new ConcreteSubSystemA();
subSystemB = new ConcreteSubSystemB();
}
// 提供给客户端的接口
public void operation() {
subSystemA.operation();
subSystemB.operation();
}
}
// 子系统接口
interface SubSystemInterface {
void operation();
}
// 具体的子系统A
class ConcreteSubSystemA implements SubSystemInterface {
public void operation() {
// 实现具体的功能
}
}
// 具体的子系统B
class ConcreteSubSystemB implements SubSystemInterface {
public void operation() {
// 实现具体的功能
}
}
多个门面类
- 当门面已经庞大到不能忍受的程度,承担过多的责任时,可以考虑使用多个门面类。
- 每个门面类负责与特定的子系统交互,原则上建议按照功能拆分
比如,一个数据库操作的门面可以拆分为查询门面、删除门面、更新门面等。
// 子系统A的门面类
class SubSystemAFacade {
private SubSystemA subSystemA;
public SubSystemAFacade() {
subSystemA = new SubSystemA();
}
// 提供给客户端的接口
public void operation() {
subSystemA.operationA();
}
}
// 子系统B的门面类
class SubSystemBFacade {
private SubSystemB subSystemB;
public SubSystemBFacade() {
subSystemB = new SubSystemB();
}
// 提供给客户端的接口
public void operation() {
subSystemB.operationB();
}
}
通过上述优化实现方式,我们能够灵活地应对不同的需求和场景,提高了系统的可扩展性和维护性。
门面嵌套
- 假设我们有一个文件处理系统,其中包括3个子系统:
文件读取(FileReader)、文件写入(FileWriter)和文件压缩(FileCompressor)。
- 现在有2个模块来访问该子系统:
- 通用模块(GeneralModule)可以完整地访问所有业务逻辑,而受限模块(RestrictedModule)只能访问文件读取操作。
- 在这种情况下,我们可以在门面外再嵌套门面来解决接口权限问题,以供不同的模块访问。
// 子系统:文件读取
class FileReader {
public void read(String filePath) {
System.out.println("读取文件:" + filePath);
// 具体的读取逻辑...
}
}
// 子系统:文件写入
class FileWriter {
public void write(String filePath, String content) {
System.out.println("写入文件:" + filePath);
// 具体的写入逻辑...
}
}
// 子系统:文件压缩
class FileCompressor {
public void compress(String filePath, String destinationPath) {
System.out.println("压缩文件:" + filePath + " -> " + destinationPath);
// 具体的压缩逻辑...
}
}
// 通用模块门面
class GeneralFacade {
private FileReader fileReader;
private FileWriter fileWriter;
private FileCompressor fileCompressor;
public GeneralFacade() {
this.fileReader = new FileReader();
this.fileWriter = new FileWriter();
this.fileCompressor = new FileCompressor();
}
public void processFile(String filePath, String content, String destinationPath) {
fileReader.read(filePath);
fileWriter.write(filePath, content);
fileCompressor.compress(filePath, destinationPath);
}
public void read(String filePath) {
fileReader.read(filePath);
}
}
// 受限模块门面
class RestrictedFacade {
private GeneralFacade generalFacade = new GeneralFacade();
public void readRestrictedFile(String filePath) {
generalFacade.read(filePath);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
GeneralFacade generalFacade = new GeneralFacade();
generalFacade.processFile("file.txt", "Hello World!", "compressed.zip");
RestrictedFacade restrictedFacade = new RestrictedFacade();
restrictedFacade.readRestrictedFile("file.txt");
}
}
- 在上述示例中,我们使用了2个不同的门面:
GeneralFacade和RestrictedFacade。
GeneralFacade提供了完整的访问子系统的方法(processFile)- 而
RestrictedFacade仅提供了受限的文件读取方法(readRestrictedFile)
通过不同的门面对象,通用模块可以访问所有子系统功能,而受限模块只能访问特定的子系统功能。
案例实践
CASE 门面模式的简单实现
SubSystemA / SubSystemB
- 子系统A
// 子系统A
public class SubSystemA {
public void operationA() {
System.out.println("子系统A的操作");
}
}
- 子系统B
public class SubSystemB {
public void operationB() {
System.out.println("子系统B的操作");
}
}
- 子系统C
public class SubSystemC {
public void operationC() {
System.out.println("子系统C的操作");
}
}
Facade/门面类
public class Facade {
private SubSystemA subSystemA;
private SubSystemB subSystemB;
private SubSystemC subSystemC;
public Facade() {
subSystemA = new SubSystemA();
subSystemB = new SubSystemB();
subSystemC = new SubSystemC();
}
// 提供简单的接口给客户端调用,隐藏了子系统的复杂性
public void operation() {
subSystemA.operationA();
subSystemB.operationB();
subSystemC.operationC();
}
}
CASE 电商系统
- 场景描述:
假设我们的电子商务系统包含了订单管理、库存管理和支付管理等子系统。
为了简化客户端的调用过程,我们可以使用门面模式来封装这些子系统,并提供一个统一的接口。
OrderService/订单管理子系统
// 订单管理子系统
class OrderService {
public void createOrder() {
// 创建订单的具体实现
}
}
InventoryService/库存管理子系统
// 库存管理子系统
class InventoryService {
public void checkStock() {
// 检查库存的具体实现
}
}
PaymentService/支付管理子系统
// 支付管理子系统
class PaymentService {
public void makePayment() {
// 支付的具体实现
}
}
ECommerceFacade/电子商务门面类
// 电子商务门面类
class ECommerceFacade {
private OrderService orderService;
private InventoryService inventoryService;
private PaymentService paymentService;
public ECommerceFacade() {
orderService = new OrderService();
inventoryService = new InventoryService();
paymentService = new PaymentService();
}
// 提供给客户端的接口
public void placeOrder() {
orderService.createOrder();
inventoryService.checkStock();
paymentService.makePayment();
}
}
- 我们创建了一个电子商务门面类(ECommerceFacade),它封装了订单管理、库存管理和支付管理等子系统,并提供了一个简单的接口(
placeOrder)供客户端调用。
这样,客户端只需要通过门面类来完成下单操作,而无需直接与子系统交互。
CASE Shape(形状接口) 与 ShapeMaker(形状创建器外观类)
场景描述
- 我们将创建一个
Shape接口和实现了Shape接口的实体类。下一步是定义一个外观类ShapeMaker。 ShapeMaker类使用实体类来代表用户对这些类的调用。FacadePatternDemo类使用ShapeMaker类来显示结果。

Shape :抽象接口
public interface Shape {
void draw();
}
Rectangle / Square :具体的接口实现类(子系统)
- Rectangle
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Rectangle::draw()");
}
}
- Square
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Square::draw()");
}
}
- Circle
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Circle::draw()");
}
}
ShapeMaker(外观类)
public class ShapeMaker {
private Shape circle;
private Shape rectangle;
private Shape square;
public ShapeMaker() {
circle = new Circle();
rectangle = new Rectangle();
square = new Square();
}
public void drawCircle(){
circle.draw();
}
public void drawRectangle(){
rectangle.draw();
}
public void drawSquare(){
square.draw();
}
}
FacadePatternDemo
- 使用该外观类画出各种类型的形状。
public class FacadePatternDemo {
public static void main(String[] args) {
ShapeMaker shapeMaker = new ShapeMaker();
shapeMaker.drawCircle();
shapeMaker.drawRectangle();
shapeMaker.drawSquare();
}
}
out
Circle::draw()
Rectangle::draw()
Square::draw()
Y 推荐文献
X 参考文献
[设计模式/Java] 设计模式之门面模式(外观模式)【20】的更多相关文章
- javascript设计模式(张容铭)学习笔记 - 外观模式绑定事件
有一个需求要为document对象绑定click事件来是想隐藏提示框的交互功能,于是小白写了如下代码: document.onclick = function(e) { e.preventDefaul ...
- java设计模式--结构型模式--外观模式
外观模式 概述 为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. 适用性 1.当你要为一个复杂子系统提供一个简单接口时.子系统往往因为不 ...
- 设计模式 结构型模式 外观模式(Facade Pattern)
在软件开发过程中,客户端程序经常会与复杂系统的内部子系统进行耦合,从而导致客户端程序随着子系统的变化而变化. 这时为了将复杂系统的内部子系统与客户端之间的依赖解耦,从而就有了外观模式,也称作 ”门面“ ...
- Android设计模式(九)--外观模式
问题:在Android中,Apk能够有微信,QQ为代表的插件式安装更新功能: 那么问题来了,主系统(姑且这么说)调用插件式安装的子系统.由子系统提供对外的訪问.属不属于一种外观模式呢? 先说设计模式: ...
- headfirst设计模式(8)—适配器模式与外观模式
前言 这一章主要讲2个模式,一个是,适配器模式(负责将一个类的接口适配成用户所期待的),另外一个是外观模式(为子系统提供一个共同的对外接口),看完的第一反应是,为什么要把它们两放在同一章,难道它们有什 ...
- 设计模式(十一):FACADE外观模式 -- 结构型模式
1. 概述 外观模式,我们通过外观的包装,使应用程序只能看到外观对象,而不会看到具体的细节对象,这样无疑会降低应用程序的复杂度,并且提高了程序的可维护性.例子1:一个电源总开关可以控制四盏灯.一个风扇 ...
- 大话设计模式C++达到-文章12章-外观模式
一.UML画画 关键词:添加Facade层. 二.概念 外观模式:为子系统中的一组接口提供一个一致的界面.此模式定义了一个高层接口,这个接口使得这一子系统更加easy使用. 三.说明 Q:外观模式在什 ...
- 《大话设计模式》ruby版代码:外观模式
需求: 股民买卖股票 初步代码: # -*- encoding: utf-8 -*- #股票1 class Stock1 def buy puts '股票1买入' end def sell puts ...
- php 23种设计模型 - 门面模式(外观模式)
外观模式(Facade) 外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口.这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系 ...
- 结构型:代理模式 Vs 适配器模式 Vs 门面模式(外观模式)
先上UML图 代理模式: 适配器模式: 门面模式(外观模式): 打了例子……呃……举个比方 代理模式: 水浒街,西门庆看上潘金莲,想和她嘿咻嘿咻,但是自己有不能去找潘金莲去说,于是他找到了金牌代理人王 ...
随机推荐
- 从生活案例理解滑动窗口最大值:一个超直观的思路讲解|LeetCode 239 滑动窗口最大值
LeetCode 239 滑动窗口最大值 点此看全部题解 LeetCode必刷100题:一份来自面试官的算法地图(题解持续更新中) 更多干货,请关注公众号[忍者算法],回复[刷题清单]获取完整题解目录 ...
- CentOS7安装nvm和node
一.安装nvm 官方文档:https://github.com/nvm-sh/nvmwindows版文档:https://github.com/coreybutler/nvm-windowswindo ...
- 清华大学推出的 DeepSeek 从入门到精通(104页)免费教程!
前言 最近 DeepSeek 的出现让 AI 在国内掀起了一股浪潮,各大媒体.平台都在讨论和推广 DeepSeek,帮助各行各样使用 AI 不再有困难.今天大姚给大家分享一个由清华大学推出的.免费的: ...
- FreeSql学习笔记——6.修改
前言 FreeSql 提供丰富的数据库更新功能,支持单条或批量更新,支持更新指定的字段,在特定的数据库执行还可以返回更新后的记录.与删除一样,没有条件的话不会执行,避免全表修改到全表: 指 ...
- Vulnhun靶机-kioptix level 4-sql注入万能密码拿到权限ssh连接利用mysql-udf漏洞提权
一.环境搭建 然后选择靶机所在文件夹 信息收集 本靶机ip和攻击机ip 攻击机:192.168.108.130 靶机:192.168.108.141 扫描ip 靶机ip为:192.168.108.14 ...
- OpenLayers change 事件获取当前值
这里有个 change:resolution 事件 但是事件的内容没有value,只有oldValue Zc {type: 'change:resolution', target: F, key: ' ...
- 关于Convert.ToUInt16(string? value, int fromBase);
例子: static void Main(string[] args) { string x = "17"; ushort hex = Convert.ToUInt16(x, 16 ...
- 「二」nginx下载与安装
1.下载地址(开源版):https://nginx.org/en/download.html wget https://nginx.org/download/nginx-1.14.2.tar.gz 2 ...
- 【编程思维】临近实施 WPF 下拉框闪烁问题!!
私以为架构是业务开发的发展历史,顺应大方向而生,再为贴切时刻的用户需求,持续微改动. 我本以为了解这个软件的架构没甚意思,加快的开发速度不能过渡到下一个别的软件去: 却不知以小窥大,关键还是计算机思维 ...
- Gits-命令
Git基础命令 Git是一个分布式版本控制系统,由Linus Torvalds创建,用于有效.高速地处理从小到大的项目版本管理.以下是一些基本的Git命令和概念,它们对于使用Git进行版本控制至关重要 ...