Java设计模式之外观模式详细讲解和案例示范

news2024/11/13 10:19:42
1. 引言

在软件开发过程中,复杂的系统往往包含许多子系统和模块,随着系统功能的增加,模块之间的交互也变得更加复杂。这种复杂性可能会导致系统的可维护性和扩展性降低。外观模式(Facade Pattern)是一种结构型设计模式,通过提供一个简化的接口,将复杂的子系统隐藏在幕后,使得外部客户端可以更轻松地与系统进行交互。
本文将详细讲解外观模式的定义、使用场景、常见问题及其解决方案,我们还将深入探讨外观模式与代理模式、装饰模式、适配器模式的区别。最后,以电商交易系统为案例,展示外观模式的实际应用。

2. 外观模式概述
2.1 定义

外观模式通过为子系统中的一组接口提供一个统一的接口,使得这些子系统更容易使用。外观模式并不限制子系统本身的功能和内部结构,而是为客户端提供一个简单的入口,使得客户端不需要了解子系统的复杂性。

2.2 优点
  • 简化接口:外观模式通过为复杂的子系统提供简化的接口,使得系统更容易理解和使用。
  • 降低耦合度:客户端通过外观模式与子系统进行交互,减少了对子系统内部的直接依赖,从而降低了系统的耦合度。
  • 增强可维护性:由于客户端与子系统之间的耦合度降低,子系统的变化不会直接影响到客户端,提高了系统的可维护性。
2.3 缺点
  • 增加额外层次:外观模式增加了一个额外的抽象层次,如果子系统接口已经很简单,使用外观模式可能显得多余。
  • 隐藏细节:虽然外观模式简化了接口,但同时也隐藏了子系统的细节,对于需要深度理解系统的开发者来说,可能不够透明。
3. 外观模式的使用场景

外观模式在以下场景中非常适用:

  • 简化复杂子系统的接口:当一个子系统非常复杂,外部客户端难以直接使用时,可以使用外观模式简化接口。
  • 模块解耦:当需要将客户端与多个子系统模块解耦时,外观模式可以通过提供一个统一的接口来实现。
  • 分层系统中的入口点:在分层架构中,外观模式可以作为每个子系统或层的入口点,从而隔离客户端与子系统的直接交互。
4. 常见问题及解决方案
4.1 如何设计合理的外观接口?

在设计外观接口时,应尽量考虑客户端的实际需求,将复杂的子系统操作简化为客户端易于理解和调用的接口。避免设计过于臃肿的外观接口,应将功能分离到多个外观类中。

4.2 外观模式会不会隐藏太多细节?

外观模式的目的是简化接口,而不是完全隐藏系统的细节。如果客户端需要访问子系统的特定功能,可以通过外观模式提供扩展方法,或者允许客户端直接访问子系统。

4.3 如何在现有系统中引入外观模式?

在现有系统中引入外观模式时,可以先分析系统的复杂度和客户端的需求,确定哪些功能可以通过外观模式进行简化。然后逐步引入外观类,将子系统的复杂性隐藏在外观模式之后。

5. 外观模式的实际应用:以电商交易系统为例
5.1 电商交易系统中的外观模式

在电商交易系统中,订单处理、支付处理和物流管理是三个相对独立的子系统。为了简化客户端的操作,我们可以设计一个ECommerceFacade类,提供统一的接口来管理这些复杂的子系统。

5.2 代码示例
// 订单子系统
class OrderService {
    public void createOrder(String productId, int quantity) {
        System.out.println("Creating order for product " + productId + " with quantity " + quantity);
    }
}

// 支付子系统
class PaymentService {
    public void processPayment(String orderId, double amount) {
        System.out.println("Processing payment for order " + orderId + " with amount " + amount);
    }
}

// 物流子系统
class ShippingService {
    public void arrangeShipping(String orderId) {
        System.out.println("Arranging shipping for order " + orderId);
    }
}

// 外观类
class ECommerceFacade {
    private OrderService orderService;
    private PaymentService paymentService;
    private ShippingService shippingService;

    public ECommerceFacade() {
        this.orderService = new OrderService();
        this.paymentService = new PaymentService();
        this.shippingService = new ShippingService();
    }

    public void completeOrder(String productId, int quantity, double amount) {
        orderService.createOrder(productId, quantity);
        String orderId = "ORDER123";  // 模拟订单ID
        paymentService.processPayment(orderId, amount);
        shippingService.arrangeShipping(orderId);
    }
}

// 客户端代码
public class ECommerceApp {
    public static void main(String[] args) {
        ECommerceFacade ecommerceFacade = new ECommerceFacade();
        ecommerceFacade.completeOrder("PROD001", 2, 199.99);
    }
}
5.3 类图

在这里插入图片描述

通过这张类图,我们可以清楚地看到外观模式如何将复杂的子系统封装在ECommerceFacade类中,客户端只需通过外观类的接口即可完成订单的创建、支付和物流安排。

6. 外观模式与其他设计模式的区别
6.1 外观模式 vs 代理模式
  • 外观模式:外观模式通过提供一个简化的接口来隐藏子系统的复杂性,目的是减少客户端与子系统之间的耦合。
  • 代理模式:代理模式为另一个对象提供代理或占位符,以控制对该对象的访问。代理模式的重点是控制,而外观模式的重点是简化接口。

外观模式和代理模式的关键区别在于它们的意图和使用目的。外观模式侧重于简化接口,而代理模式侧重于控制对目标对象的访问。

6.2 外观模式 vs 装饰模式
  • 外观模式:外观模式提供了一个简化的接口,将复杂的子系统隐藏起来,目的是简化客户端的使用。
  • 装饰模式:装饰模式动态地给对象增加行为,而不改变其接口。装饰模式的重点是增强或修改对象的功能,而外观模式的重点是简化子系统的使用。

装饰模式用于在不修改对象的基础上为其添加新功能,而外观模式用于简化复杂系统的接口。

6.3 外观模式 vs 适配器模式
  • 外观模式:外观模式提供了一个简化的接口,隐藏了子系统的复杂性,主要目的是降低系统的复杂度。
  • 适配器模式:适配器模式用于将一个接口转换为另一个接口,以便于不兼容的类可以一起工作。适配器模式的重点是接口的转换,而外观模式的重点是简化系统接口。

适配器模式用于处理接口不兼容的问题,而外观模式则是通过提供简化接口来隐藏复杂的子系统。

7. Spring的外观模式实际应用

Spring框架中的DataSourceTransactionManager是外观模式在实际应用中的一个典型例子。通过外观模式,Spring将事务管理的复杂性封装在一个易于使用的接口中,让开发者能够简单地处理事务,而无需关心底层的实现细节。接下来,我们将详细介绍DataSourceTransactionManager是如何通过外观模式简化事务管理的。

7.1 背景与挑战

在一个电商系统中,操作数据库是不可避免的任务。考虑到数据一致性,特别是在处理订单、支付等关键业务时,事务管理变得非常重要。传统的事务管理需要手动处理连接的获取、事务的开始、提交、回滚等操作,这不仅繁琐,还容易出错。如果这些步骤处理不当,可能会导致数据不一致、连接泄露等问题。

7.2 Spring的事务管理

Spring通过DataSourceTransactionManager类来管理事务,它对事务管理的各个步骤进行了封装,并提供了一个统一的接口。这使得开发者可以专注于业务逻辑,而不必关心底层的事务管理细节。

DataSourceTransactionManager是Spring事务管理的核心组件之一,它实现了PlatformTransactionManager接口,通过外观模式,DataSourceTransactionManager将事务的开始、提交、回滚等操作隐藏在其内部,并通过简单的方法调用对外提供。

7.3 DataSourceTransactionManager的外观模式实现
7.3.1 类图展示

在这里插入图片描述

从类图可以看出,DataSourceTransactionManager实现了PlatformTransactionManager接口,并且封装了具体的DataSource对象,以简化对数据库事务的操作。

7.3.2 代码示例

为了更好地理解DataSourceTransactionManager如何应用外观模式,以下是一个简单的电商交易系统中使用Spring事务管理的代码示例:

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

public class TransactionManagerFacadeExample {

    public static void main(String[] args) {
        // 创建Spring上下文
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TransactionConfig.class);

        // 获取JdbcTemplate和事务管理器
        JdbcTemplate jdbcTemplate = context.getBean(JdbcTemplate.class);
        PlatformTransactionManager transactionManager = context.getBean(PlatformTransactionManager.class);

        // 开始一个新事务
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());

        try {
            // 执行数据库操作
            jdbcTemplate.update("INSERT INTO orders (id, customer_id, total) VALUES (1, 1, 100.00)");

            // 提交事务
            transactionManager.commit(status);
            System.out.println("Transaction committed successfully.");
        } catch (Exception ex) {
            // 出现异常,回滚事务
            transactionManager.rollback(status);
            System.out.println("Transaction rolled back due to an error: " + ex.getMessage());
        }

        // 关闭上下文
        context.close();
    }
}

// 事务配置类
class TransactionConfig {
    public JdbcTemplate jdbcTemplate() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/ecommerce");
        dataSource.setUsername("root");
        dataSource.setPassword("password");
        return new JdbcTemplate(dataSource);
    }

    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(jdbcTemplate().getDataSource());
    }
}
7.3.3 代码解析

在这个示例中,我们创建了一个Spring上下文,配置了一个JdbcTemplatePlatformTransactionManager(具体为DataSourceTransactionManager实现)。通过事务管理器的getTransaction()方法,我们开启了一个事务,并执行了数据库操作。如果操作成功,事务将被提交;如果操作失败,事务将被回滚。

DataSourceTransactionManager通过外观模式,将底层的事务管理操作(如获取连接、开启事务、提交或回滚事务)封装在其内部,并通过统一的接口提供给开发者使用。开发者在使用时,只需调用事务管理器的相关方法即可,不需要深入了解底层的实现。

8. 结论

外观模式是一个非常有用的设计模式,尤其适用于需要简化复杂系统接口的场景。在电商交易系统的案例中,我们通过ECommerceFacade类将订单、支付、物流等子系统的复杂性隐藏起来,简化了客户端的使用。外观模式不仅可以降低系统的耦合度,还可以提高系统的可维护性和可扩展性。

在实际开发中,我们还需要注意外观模式与其他模式的区别,以便在合适的场景中选择最优的设计模式。外观模式与代理模式、装饰模式、适配器模式在意图和实现上都有所不同,因此需要根据实际需求进行权衡和选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2086874.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【鸿蒙学习】HarmonyOS应用开发者高级认证 - 认证通过(附题目)

学完时间:2024年8月29日 学完排名:第192546名 一、前言叨叨 经过几日的休整,我终于再次挑战高级认证,并以82分的成绩堪堪越过了及格线。然而,通过考试后我惊讶地发现,原来顺利过关的人数如此众多。我逐一…

cv2图像总结

我今天发现cv2读进来的图像是BRG格式的,和其他的方式不同 import cv2 import matplotlib.pyplot as plt image_path "./GSE240429_data/image/GEX_C73_A1_Merged.tiff" img1 cv2.imread(image_path) print(img1.shape) plt.imshow(img1, cmapgray) …

MariaDB VS MySQL

MariaDB和MySQL是两种流行的开源关系型数据库管理系统(RDBMS),它们在功能、性能、兼容性、开源性以及社区支持等方面各有特点。以下是对两者主要区别的详细分析: 1. 开发者与起源 MySQL:自1995年问世以来&#xff0c…

白银现货的两大指标,如何使用?

在白银现货交易的过程中,我们会借助大量的技术指标,对现货白银走势进行分析,找到买点和卖点,可以说,技术指标对我们的白银现货交易起到很好的辅助作用,也是我们阅读白银市场很好的工具。本文将和大家讨论一…

一个非常实用的Win系统瘦身项目,PowerShell脚本支持Windows 11跟10,非常轻量好用(附源码)

Win经常我们都经常用,但系统里总是预装了一些我们可能并不需要的应用程序。这些应用不仅占用了宝贵的存储空间,还可能拖慢了我们的电脑速度。特别是Windows 11,一些花里胡哨的功能和后台服务,让我们的电脑变得不那么“清爽”。 今…

N10 - NLP中的注意力机制

🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 目录 1. 注意力机制是什么2. 注意力实现步骤0. 准备隐藏状态1. 获取每个编码器隐藏状态的分数2. 通过softmax层运行所有分数3. 通过softmax得分将每个编码器的…

elasticsearch之我不会的

elasticsearch之我不会的 如何安装,在此不谈,开门见山 1.概念理解 Relational DBelasticsearch说明表tableindex索引(index),就是文档的集合,类似数据库的表(table)行rows文档documents文档(Document)&a…

51.x86游戏实战-XXX返回城镇的实现

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 本次游戏没法给 内容参考于:微尘网络安全 工具下载: 链接:https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

使用智谱AI大模型翻译视频字幕

不久前,国内的头部大模型厂商智谱 AI ,刚刚推出了 glm-4-0520 模型,该模型被认为是当前平台最先进的模型,具备 128k 的上下文长度,并且相较于前一代模型,指令遵从能力大幅提升 18.6%。可以看出,…

一键开启,精彩即现!极简设计录屏软件大盘点

如果你想要用一款小巧的录屏工具,第一时间是不是就想到了ocam录屏,现在这类的简便录屏工具越来越多了,如果你想要换一个不妨接着往下看吧。 1.福昕录屏大师 链接:www.foxitsoftware.cn/REC/ 这个软件的界面看起来就很好操作&am…

《HelloGitHub》第 101 期

兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 github.com/521xueweihan/HelloGitHub 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等,涵盖多种编程语言 Python、…

测试 UDP 端口可达性的方法

前言: UDP (User Datagram Protocol) 是一种无连接的传输层协议,它不像 TCP 那样提供确认机制来保证数据包的可靠传输。因此,测试 UDP 端口的可达性通常需要一些特殊的方法,因为传统的端口扫描工具(如 nmap&#xff0…

【开源 Mac 工具推荐之 5】tldr:简洁明了的命令行手册显示工具

简介 在大家日常在 macOS/Linux 上使用 Shell 的时候&#xff0c;常常会遇到一些不太熟悉的命令行指令&#xff0c;为此我们一般会查看一下该命令的使用手册&#xff08;指南&#xff09;。往往&#xff0c;大家都会使用 man <command> 这样一个非常传统的指令。但 man …

YOLOv8改进 | 融合改进 | C2f融合Faster-GELU模块提升检测速度【完整代码 + 主要代码解析】

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录 &#xff1a;《YOLOv8改进有效…

Unity URPShader支持多光源处理

//声明变体并且引用文件 #pragma shader_feature _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" //在数据结构体中声明需要使用的数据 struct Attributes {float4 posit…

如何写一份简单的3C产品说明书?五步让你留住客源

在撰写3C&#xff08;计算机、通信、消费电子&#xff09;产品说明书时&#xff0c;清晰、简洁且全面的信息传递至关重要。本文将首先介绍产品说明书的基本结构&#xff0c;随后探讨视觉阅读的重要性&#xff0c;并通过实例展示如何撰写一份优秀的产品说明书。最后&#xff0c;…

SpringBoot2:学SpringBoot前的知识准备-用IDEA创建传统的webapp工程,并整合SpringMVC

1、IDEA创建工程 基于Maven模板创建的SpringMVC工程 工程创建好后&#xff0c;只有webapp目录 这里&#xff0c;我们需要手动创建java目录和resources配置文件目录 创建好后&#xff0c;配置下目录属性 最终结构 至此&#xff0c;工程就创建好了 2、配置Tomcat 参考&am…

LabVIEW中10μs方波生成问题

在LabVIEW中使用NI PCIe-6353卡生成并控制10μs级别的方波输出可能遇到频率调整的问题。下面将详细分析常见问题的原因&#xff0c;如采样率设置、时钟源配置、波形生成方式等&#xff0c;并提供具体的解决方案&#xff0c;帮助用户成功生成并调整高精度方波信号。 为了在LabVI…

孙悟空求药的深刻反思

孙悟空求药的深刻反思 - 孔乙己大叔引言&#xff1a;孙悟空的求药之旅 在古老的神话故事中&#xff0c;孙悟空为了拯救乌鸡国国王的生命&#xff0c;不惜跨越千山万水&#xff0c;前往太上老君的仙宫&#xff0c;祈求那传说中的九转还魂丹。面对孙悟空那看似贪婪的一千…