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

embedded/2024/12/28 14:38:58/

在这里插入图片描述

~犬📰余~

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

一、外观模式概述

\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();        // 启动CPUmemory.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.ppmy.cn/embedded/148943.html

相关文章

Jmeter自学【8】- 使用JMeter模拟设备通过MQTT发送数据

今天使用jmeter推送数据到MQTT,给大家分享一下操作流程。 一、安装JMeter 参考文档:Jmeter自学【1】- Jmeter安装、配置 二、安装MQTT插件 1、下载插件 我的Jmeter版本是5.6.3,用到的插件是:mqtt-xmeter-2.0.2-jar-with-depe…

Object.prototype.hasOwnProperty.call(item, key) 作用与用途

在 JavaScript 中,Object.prototype.hasOwnProperty.call(item, key) 是一种检查对象 item 是否具有特定属性 key 作为自身的属性(而不是继承自原型链)的方法。这种调用方式是安全的,特别是在处理可能被修改过原型链的对象时。 解…

css省略号后面跟图标

纯css实现省略号后面跟图标 不溢出图标跟在文字后面&#xff0c;超过两行省略号表示&#xff0c;并在后面跟图标 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"…

SpringAI人工智能开发框架006---SpringAI多模态接口_编程测试springai多模态接口支持

可以看到springai对多模态的支持. 同样去创建一个项目 也是跟之前的项目一样,修改版本1.0.0 这里 然后修改仓库地址,为springai的地址 然后开始写代码

【每日学点鸿蒙知识】上架流程、h5返回收拾拦截、两个枚举类型之间转换、hvigorw命令、绘制本地图片

1、HarmonyOS 上架流程&#xff1f; 上架流程&#xff0c;请参考文档&#xff1a;https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-publish-app-V5 上架的一些条件&#xff0c;也请 这边悉知&#xff0c;参考链接&#xff1a;https://developer.huawe…

加电:DETR论文阅读

DETR&#xff1a;End ot End object detection with transformer. 主要工作 这篇文章的主要工作在于&#xff0c;是将transformer引入到目标检测这一类计算机视觉的任务中&#xff0c;transformer的优势在于&#xff1a;模型的通用性和高上限(gpt是最好的例子&#xff0c;算力…

软件工程课程知识点

一、软件与软件工程概述 1. 软件的组成与演化 软件的构成 Software(软件) 通常由 computer programs(计算机程序)、data structures(数据结构)、software description information(软件描述信息) 组成&#xff0c;或者说由 set of programs(程序集合)、documentation(文档) …

【首发1day详情】CVE-2024-51479 全网首发漏洞复现分析+POC (Next.js权限绕过)

绪论 如果各位师傅觉得有用的话&#xff0c;可以给我点个关注~~ 如果师傅们有什么好的建议也欢迎联系我~~ 感谢各位师傅的支持~~ 正文部分 声明 1. 本漏洞根据网上的资料和我自己的理解去复现&#xff0c;并不确定就是cve-2024-51479的最终细节2. 尝试在互联网进行复现&#xf…