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

设计模式之外观模式:从电脑组装到系统架构的简化之道

在这里插入图片描述

~犬📰余~

“我欲贱而贵,愚而智,贫而富,可乎?
曰:其唯学乎”

一、外观模式概述

\quad 在软件开发中,我们经常会遇到一些复杂的系统,这些系统可能包含许多子系统和组件。直接使用这些子系统不仅需要了解它们的工作原理,还要清楚它们之间的调用关系。这就像是你要维修一台复杂的机器,必须了解每个零件的作用和装配顺序一样。而外观模式就是为了解决这个问题而生的,它就像是给这台复杂的机器配了一个使用说明书,让你不用了解内部结构也能轻松使用。
\quad 外观模式的本质是:提供一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。就像我们在餐厅点餐,不需要关心厨师是如何采购食材、如何烹饪的,我们只需要向服务员点餐就可以了,服务员就是这里的"外观"。
\quad 让我们通过一张图来理解外观模式的基本结构:
在这里插入图片描述
\quad 从图中我们可以看到,外观模式主要包含两个部分:一个是统一对外的外观类(Facade),另一个是各个子系统类(SubSystem)。客户端只需要与外观类打交道,而不需要直接与各个子系统交互。这样不仅简化了客户端的调用,还降低了客户端与子系统之间的耦合度。
\quad 举个生活中的例子,当你想组装一台电脑时,你可以选择自己去电脑城买各种配件然后组装,这需要你对每个配件都有所了解。但你也可以直接去找电脑店的销售人员,告诉他你的需求,他会帮你选择合适的配件并组装好。在这个例子中,销售人员就扮演了外观的角色,他帮你屏蔽了组装电脑所需的复杂细节。

二、外观模式的角色组成

\quad 外观模式的结构相对简单,主要由外观(Facade)角色和子系统(SubSystem)角色组成。让我们通过前面的电脑组装的例子来详细了解这些角色。

在这里插入图片描述

  • 外观角色(Facade)是外观模式的核心,它知道所有子系统的功能和职责,就像电脑店的销售人员了解每个电脑配件的作用一样。外观类的主要职责是简化接口,它会将客户端的请求委派给一个或多个子系统进行处理。在我们的例子中,ComputerFacade就是一个外观类,它封装了组装电脑的复杂过程,为客户端提供了一个简单的startComputer()方法。
  • 子系统角色(SubSystem)是实现系统功能的各个类的集合。这些类本身是一个个功能独立的模块,就像电脑的各个配件:CPU、内存、硬盘等。每个子系统都不知道外观的存在,它们只是专注于完成自己的工作。在类图中,我们可以看到CPU、Memory、HardDisk这些子系统类都有自己的属性和方法,它们各司其职,共同实现了计算机的功能。

\quad 从角色之间的关系来看,外观类和各个子系统类之间是组合关系,外观类会持有子系统类的实例。而各个子系统类之间可能存在相互调用的关系,但它们都不会直接和客户端打交道,所有的交互都通过外观类来进行。这种结构确保了系统的高内聚低耦合,使得系统更容易维护和扩展。

三、外观模式案例

\quad 让我们通过一个具体的例子来深入理解外观模式。还是以组装电脑的场景为例,我们将实现一个简化的计算机启动程序。在这个例子中,我们有CPU、内存、硬盘这三个核心部件,它们都有自己的初始化过程。我们将使用外观模式来封装这些复杂的初始化过程,为用户提供一个简单的启动电脑的方法。
\quad 首先,让我们看看各个子系统类的实现:

// CPU子系统
public class CPU {

    private boolean frozen;

    public void start() {
        System.out.println("CPU开始启动...");
        this.frozen = false;
        System.out.println("CPU已就绪");
    }
}

// 内存子系统
public class Memory {

    private int capacity;

    public void load() {
        System.out.println("内存开始加载数据...");
        System.out.println("内存加载完成");
    }
}

// 硬盘子系统
public class HardDisk {

    private int size;
    
    public void read() {
        System.out.println("硬盘开始读取数据...");
        System.out.println("硬盘读取完成");
    }
}

\quad 接下来,我们创建外观类(ComputerFacade),它将封装所有子系统的调用:

public class ComputerFacade {
    private CPU cpu;
    private Memory memory;
    private HardDisk hardDisk;

    public ComputerFacade() {
        this.cpu = new CPU();
        this.memory = new Memory();
        this.hardDisk = new HardDisk();
    }

    public void startComputer() {
        System.out.println("开始启动电脑...");
        cpu.start();        // 启动CPU
        memory.load();      // 加载内存
        hardDisk.read();    // 读取硬盘数据
        System.out.println("电脑启动完成!");
    }
}

\quad 最后,我们来看看客户端如何使用这个外观类:

public class Client {
    public static void main(String[] args) {
        // 创建外观类实例
        ComputerFacade computer = new ComputerFacade();
        // 启动电脑
        computer.startComputer();
    }
}

\quad 运行这段代码,我们会看到如下输出:
在这里插入图片描述
\quad 从上面的例子中,我们可以看到外观模式的工作流程:客户端只需要与ComputerFacade打交道,调用一个简单的startComputer()方法就可以完成电脑的启动。而在背后,ComputerFacade则负责协调CPU、内存和硬盘这三个子系统,按照正确的顺序执行它们的操作。这个过程正如我们在时序图中看到的那样,外观类在接收到客户端的请求后,会依次调用各个子系统的方法。
\quad 这样的设计大大简化了客户端的使用,客户端不需要知道电脑启动的具体细节,也不需要了解各个组件之间的依赖关系。如果将来需要修改启动流程或者增加新的组件,我们只需要修改ComputerFacade类,而不会影响到客户端的代码。

四、外观模式的优缺点

\quad 通过前面的电脑组装案例,我们已经看到了外观模式在实际应用中的表现。现在让我们来分析一下使用外观模式带来的优势和可能存在的问题。

4.1. 优点

\quad 外观模式最显著的好处是简化了客户端和子系统之间的关系。就像我们在电脑组装的例子中看到的,客户端不需要了解CPU、内存、硬盘等组件的工作原理,只需要通过外观类提供的简单接口就能实现想要的功能。这种设计不仅降低了系统的使用难度,还减少了客户端与子系统之间的依赖关系,使得系统更容易维护和扩展。
\quad 从系统维护的角度来看,外观模式提供了一个统一的访问入口,这意味着如果子系统需要改变,我们只需要修改外观类,而不会影响到众多的客户端代码。比如在我们的例子中,如果要改变电脑启动的流程,或者增加新的硬件组件,只需要修改ComputerFacade类即可,客户端的代码可以保持不变。

4.2. 缺点

\quad 最主要的问题是外观类可能会变得过于复杂。随着系统的发展,如果我们不断地往外观类中添加新的功能,它可能会变成一个巨大的上帝类,违背了"单一职责原则"。就像一个处理过多事务的管理者,可能会成为系统的瓶颈。
\quad 另外,虽然外观模式能够屏蔽子系统的复杂性,但有时候这种屏蔽可能会过度。如果客户端需要更灵活地控制子系统,外观模式提供的简化接口可能就显得不够用了。这就像我们的电脑例子,如果用户需要对某个组件进行特殊的设置,仅仅使用startComputer()方法可能就无法满足需求了。

五、外观模式的适用场景

\quad 了解了外观模式的优缺点,我们来看看在实际开发中,什么情况下适合使用外观模式。总的来说,当我们面对一个复杂的系统,需要为客户端提供一个简单的接口时,外观模式就能发挥它的价值。

  • 外观模式特别适合用在系统分层的场景中。在一个大型系统中,我们通常会按照不同的职责将系统分成多个子系统或模块。比如在一个电商系统中,我们可能有订单管理、库存管理、支付管理等多个子系统。这时候,我们可以为每个层次提供一个外观类,用来协调子系统之间的交互。这样不仅能够简化系统的使用,还能够实现良好的层次划分。
  • 当一个系统需要对外提供API时,外观模式也是一个很好的选择。通过外观类,我们可以将系统内部复杂的实现细节隐藏起来,只暴露必要的接口给外部使用。这就像我们常用的一些SDK,它们通常会提供一个简单的API层,而在背后可能调用了很多复杂的底层服务。
  • 在进行系统重构时,外观模式也能派上用场。如果我们需要保持原有系统的接口,但要重构内部实现,就可以使用外观模式。外观类可以作为新旧系统的桥梁,在内部将请求转发到新的实现上,而客户端则可以继续使用原有的接口,不需要做任何修改。

\quad 不过,也有一些情况不适合使用外观模式。比如,如果系统本身就很简单,或者客户端需要直接控制子系统的行为,使用外观模式反而会增加不必要的复杂性。此外,如果一个外观类需要处理太多的职责,我们可能需要考虑是否应该将其拆分成多个更小的外观类。

六、总结

\quad 外观模式是一个在实际开发中非常实用的设计模式,它通过提供一个统一的接口来简化复杂系统的使用。就像我们在电脑组装的例子中看到的,它能够有效地降低系统的使用难度,同时提高系统的可维护性。
\quad 在实践中使用外观模式时,我们需要注意以下几点:首先,要合理控制外观类的粒度,既不能过于简单而失去了封装的价值,也不能过于复杂而违背了单一职责原则。其次,要根据实际需求来决定是否使用外观模式,不要为了使用设计模式而使用设计模式。最后,在设计外观类的接口时,要站在用户的角度思考,提供真正有用的简化接口。
\quad 外观模式的精髓在于"简化",但简化并不意味着功能的削减,而是通过合理的封装,让复杂的系统变得简单易用。正如古人说的"大道至简",好的设计应该能够化繁为简,让使用者能够轻松地完成他们的任务。在日常开发中,我们要善于发现可以简化的地方,合理地使用外观模式,让我们的系统变得更加优雅和易用。
在这里插入图片描述

关注犬余,共同进步

技术从此不孤单

http://www.kler.cn/a/451924.html

相关文章:

  • python fastapi docs UI 失效解决方案
  • linux命令中cp命令-rf与-a的差别
  • Anton和Danik的棋局对决
  • GPUStack v0.4.1 单节点与多节点安装与部署指南 Docker PowerShell
  • 直流有刷电机多环控制(PID闭环死区和积分分离)
  • PostgreSQL JOIN
  • 软考:系统架构设计师教材笔记(持续更新中)
  • 记录一个SVR学习
  • 【全开源】Java多语言tiktok跨境商城TikTok内嵌商城送搭建教程
  • uniApp打包H5发布到服务器(docker)
  • 3.3.2.5 队列之 ringBuffer设计
  • 关于高级acl的配置和讲解
  • Maven 项目文档
  • 微信小程序页面之间的传值方式
  • Linux 日志监控与报警系统实操
  • python+PyPDF2实现PDF的文本内容读取、多文件合并、旋转、裁剪、缩放、加解密、添加水印
  • YOLOv11融合U-Net v2中的SDI模块
  • MySQL索引为什么是B+树
  • 元宇宙中的去中心化应用:Web3的未来角色
  • 验证回文串 - 简单
  • Text组件的用法
  • 【vue】vite + ts +vue3 安装pinia
  • 创新技术集成:Web机票管理系统的未来趋势
  • 基于CNN-BiLSTM-selfAttention混合神经网络的多分类预测【MATLAB】
  • C语言从入门到放弃教程
  • MFC/C++学习系列之简单记录12——文件操作