[设计模式/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图 代理模式: 适配器模式: 门面模式(外观模式): 打了例子……呃……举个比方 代理模式: 水浒街,西门庆看上潘金莲,想和她嘿咻嘿咻,但是自己有不能去找潘金莲去说,于是他找到了金牌代理人王 ...
随机推荐
- 0425-字符输入流FileReader
package A10_IOStream; import java.awt.datatransfer.StringSelection; import java.io.IOException; impo ...
- 容器、容器云和容器化PaaS平台之间到底是什么关系?
本文分享自天翼云开发者社区<容器.容器云和容器化PaaS平台之间到底是什么关系?>,作者:s****n 一直都有很多人迷惑于容器应该属于 IaaS 或是 PaaS 层,也搞不清楚容器云到底 ...
- 解决Git报“OpenSSL SSL_read: Connection was reset, errno 10054”错的问题
1.问题描述 有时候当我们使用Git获取资源,会报"OpenSSL SSL_read: Connection was reset, errno 10054"的错误,出现该错误是因为 ...
- clickhouse 为什么如此快及优化
一.clickhouse 为什么如此快 1)优秀的代码,对性能的极致追求 clickhouse 是 CPP 编写的,代码中大量使用了 CPP 最新的特性来对查询进行加速. 2)优秀的执行引擎以及存储引 ...
- Eclipse各历史版本所需的最低JDK版本统计
Eclipse 版本名称 Version 发布时间 最低支持的jdk Kepler 4.3 2013.06 Java 6 Luna 4.4 2014.06.25 Java 7 Mars 4.5 201 ...
- Hive - [06] 行转列,列转行
行转列(多行转一行) 1.创建表,并插入示例数据. create table students_info( `SNO` string comment '学生编号', `name` string com ...
- vivo 大规模容器集群运维平台实践
作者:来自 vivo 互联网服务器团队- Zhou Qi .Kong Manyu 容器平台已经成为支持应用运维和部署的重要基础设施,当前 vivo 内部容器平台共有20+生产集群,管理数万物理机节点, ...
- [tldr]github仓库添加release
作为一个开源项目开发者,并且把自己的代码仓库托管到了github上面,所以,可以在github上提供自己的程序的release 这通常是通过二进制可执行文件的方式提供 新建草稿 点击create a ...
- 当你在浏览器中输入 google.com 后按下回车发生了什么?
按下"g"键 接下来的内容介绍了物理键盘和系统中断的工作原理,但是有一部分内容却没有涉及.当你按下"g"键,浏览器接收到这个消息之后,会触发自动完成机制.浏览器 ...
- 解决ERROR 1231 (42000): Variable 'time_zone' can't
MySQL根据配置文件会限制Server接受的数据包大小.有时候大的插入和更新会受 max_allowed_packet 参数限制,导致写入或者更新失败.(比方说导入数据库,数据表) mysql 数据 ...