漫谈设计模式 [9]:外观模式
引导性开场
菜鸟:老鸟,我最近在做一个项目,感觉代码越来越复杂,我都快看不懂了。尤其是有好几个子系统,它们之间的调用关系让我头疼。
老鸟:复杂的代码确实让人头疼。你有没有考虑过使用设计模式来简化你的代码结构?
菜鸟:设计模式?我听说过一些,但不太了解。你觉得我应该用哪个模式呢?
老鸟:听起来你的问题可能适合用**外观模式(Facade Pattern)**来解决。我们可以一起探讨一下。
渐进式介绍概念
菜鸟:外观模式?能不能解释一下是什么?
老鸟:好,我们先从一个生活中的例子说起。你买了一台新电视,电视上有很多接口,比如HDMI、USB、网线等等。如果每次你都要自己插各种线并调整设置,会很麻烦对吧?
菜鸟:是的,确实很麻烦。
老鸟:于是,你买了一个万能遥控器,这个遥控器可以帮助你一键连接所有设备并调整设置。你只需要按一个按钮,就可以享受电视和其他设备的无缝连接。
菜鸟:听起来不错,这和编程有什么关系呢?
老鸟:在编程中,外观模式就像这个万能遥控器。它提供一个简单的接口,隐藏了复杂的子系统调用。你只需要通过外观接口进行操作,而无需关心内部的复杂逻辑。
Python代码示例,逐步展开
菜鸟:能给我举个例子吗?
老鸟:当然可以。假设你有一个家庭影院系统,包括DVD播放器、投影仪和音响系统。我们先写出这几个子系统的类。
class DVDPlayer:def on(self):print("DVD Player is on")def play(self, movie):print(f"Playing {movie}")class Projector:def on(self):print("Projector is on")def set_input(self, input):print(f"Projector input set to {input}")class SoundSystem:def on(self):print("Sound System is on")def set_volume(self, level):print(f"Volume set to {level}")
菜鸟:每个子系统都有自己的方法,如果我要开电影,需要一个个调用他们的方法对吧?
老鸟:是的,这样会很麻烦。现在我们来创建一个外观类,它提供一个简单的接口来控制整个家庭影院系统。
class HomeTheaterFacade:def __init__(self, dvd_player, projector, sound_system):self.dvd_player = dvd_playerself.projector = projectorself.sound_system = sound_systemdef watch_movie(self, movie):print("Get ready to watch a movie...")self.dvd_player.on()self.dvd_player.play(movie)self.projector.on()self.projector.set_input("DVD")self.sound_system.on()self.sound_system.set_volume(5)print("Movie started!")
菜鸟:哇,这样确实简单多了!我只需要调用watch_movie
方法就行了。
老鸟:没错,我们来看看如何使用这个外观类。
dvd_player = DVDPlayer()
projector = Projector()
sound_system = SoundSystem()home_theater = HomeTheaterFacade(dvd_player, projector, sound_system)
home_theater.watch_movie("Inception")
菜鸟:代码变得清晰多了,这样维护也更容易。
问题与反思
菜鸟:如果我不使用外观模式,直接在主代码中调用子系统的方法,会有什么问题吗?
老鸟:最大的麻烦就是代码的复杂性和可维护性。每次你修改子系统的实现,都需要在主代码中找到相关的调用并修改。而使用外观模式,你只需要修改外观类的实现,主代码无需改变。
优势与适用场景
菜鸟:外观模式还有哪些优势呢?
老鸟:外观模式的主要优势包括:
- 简化接口:提供一个简单的接口,隐藏底层复杂性。
- 松散耦合:减少子系统之间的依赖关系,提高代码的可维护性。
- 更好的分层设计:有助于系统分层,使得高层代码更简洁。
适用场景包括:
- 当你有一个复杂的子系统,需要提供一个简单的接口给客户端。
- 当你希望对子系统进行解耦,使得子系统的变化不会影响到高层代码。
常见误区与优化建议
菜鸟:听起来外观模式很有用,那有没有什么常见的误区需要注意?
老鸟:有的。一个常见误区是把所有逻辑都放在外观类中,导致外观类变得臃肿。外观类应该只负责协调子系统的调用,而不应该包含太多业务逻辑。
另一个需要注意的是,外观模式并不能完全取代子系统的接口。在某些情况下,客户端可能仍然需要直接调用子系统的方法。
总结与延伸阅读
老鸟:今天我们介绍了外观模式,通过一个家庭影院的例子,你学会了如何使用外观模式来简化代码结构。外观模式的核心思想是提供一个简单的接口,隐藏底层的复杂性。
如果你感兴趣,可以继续学习其他设计模式,比如单例模式、观察者模式等。我推荐《设计模式:可复用面向对象软件的基础》这本书,里面有详细的介绍。
菜鸟:谢谢老鸟,我受益匪浅!我会继续学习其他设计模式的。
老鸟:不客气,编程的道路上我们一起进步!
相关文章:
漫谈设计模式 [9]:外观模式
引导性开场 菜鸟:老鸟,我最近在做一个项目,感觉代码越来越复杂,我都快看不懂了。尤其是有好几个子系统,它们之间的调用关系让我头疼。 老鸟:复杂的代码确实让人头疼。你有没有考虑过使用设计模式来简化你…...

多进程编程
基本概念 进程是一个具有单独功能的程序对某个数据集在处理机上的执行过程,进程也是作为资源分配的一个单位。 进程和程序是相辅相成的,进程是一个动态概念。 进程具有并行性特征。进程具有独立性和异步性。 进程的描述 进程分为三部分:…...

7-Zip压缩包如何添加密码,加密后如何取消
压缩包文件大家经常使用,最熟悉的肯定是RAR、ZIP格式压缩文件,但是7z压缩文件格式也很好用,它是三种压缩文件格式中压缩率最大的。想要将文件压缩到最小,使用7z格式可以达到最大化。那么在使用7z压缩格式的时候,我们可…...

HarmonyOS---应用测试概述
一、应用质量要求 应用质量要求分为应用体验质量建议和应用内容合规要求两大部分。 1、应用体验质量建议 功能数据完备、基础体验要求、HarmonyOS特征增强体验要求。 (1)功能数据完备 (2)基础体验要求 (3)增…...

密码学---真题演练
✨Base加密:题目-base? 靶场网址:https://polarctf.com/ Base100加密!!! 得到的新的一串密码是 rot47 密码,属于凯撒密码的一种变体. ✨斐波那契:题目-FB 从第三项开始,每一项都等…...
时间日期工具类
时间日期工具类 import java.time.*; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit;public class DateTimeUtils {private static final String DEFAULT_DATE_FORMAT "yyyy-MM-dd";private static final String DEFAULT_TIME_…...

linux中vim常用命令大全
前言 Linux有大量的配置文件,所以 Linux的文本处理工具也是比较多的,其中编辑一些配置文件时,常用的工具就是 vim。在Linux中,Vim编辑器是一个非常强大的文本编辑工具,它提供了多种模式和命令来满足不同的编辑需求。以…...

计算机的错误计算(八十九)
摘要 探讨反双曲余切函数 acoth(x) 在 附近的计算精度问题。 Acoth(x) 函数的定义为: 其中 x 的绝对值大于 1 . 例1. 计算 acoth(1.000000000002) . 不妨在 Excel 的单元格中计算,则有: 若在Python中用定义直接计算,则有几乎…...

深入理解java并发编程之aqs框架
跟synchronized 相比较,可重入锁ReentrankLock其实原理有什么不同? 所得基本原理是为了达到一个目的;就是让所有线程都能看到某种标记。synchronized通过在对象头中设置标记实现了这一目的,是一种JVM原生的锁实现方式。而Reentran…...

ubuntu配置tftp、nfs
tftp配置 tftp是简单文件传输协议,基于udp实现传输。这里的简单文件,指的是不复杂、开销不大的文件。 先在ubuntu中安装tftp,输入命令:sudo apt-get install tftp-hpa tftpd-hpa。 接着配置tftp。 输入命令:sudo v…...
Sklearn的datasets模块与自带数据集介绍
datasets 模块 用 dir() 函数查看 datasets 模块的所有属性和函数 import sklearn.datasets as datasets# 列出 sklearn.datasets 模块中的所有属性和函数 print(dir(datasets)) datasets 模块下的数据集有三种类型: (1)load系列的经典数…...

css 个人喜欢的样式 速查笔记
起因, 目的: 记录自己喜欢的, 觉得比较好看的 css. 下次用的时候,直接复制,很方便。 1. 个人 html 模板, 导入常用的 link 设置英语字体: Noto导入默认的 css使用网络 icon 图标导入 Bootstrap css 框架 html <…...
C/C++ let __DATE__ format to “YYYY-MM-DD“
C/C let DATE format to “YYYY-MM-DD” code: #include <iostream> #include <string>class compileDate {// 静态函数,用来格式化并返回编译日期 static std::string formatCompileDate() {// 编译时的日期,格式为 "MMM…...

git如何灵活切换本地账号对应远程github的两个账号
git如何灵活切换本地账号对应远程github的两个账号 问题: 有时候我们会同时维护两个github的账号里面的仓库内容,这时候本地git需要频繁的切换ssh,以方便灵活的与两个账号的仓库可以通信。这篇日记将阐述我是怎么解决这个问题的。1. 第一个账…...
Python中实现函数的递归调用
在Python中,函数的递归调用是一种非常强大且常用的编程技巧,它允许函数在其执行过程中调用自身。递归调用在解决许多问题时都显得尤为方便,比如遍历树形结构、计算阶乘、实现快速排序等。然而,递归也需要谨慎使用,因为…...

Multisim使用手册
目录 原件库: 基础元件库: 最右侧的分析仪们: 示波器: 交流分析: 操作: dB 一、幅频特性曲线 二、相频特性曲线 下载资源(有汉化):Multisim 14.0电路设计与仿真…...

线程的六种状态
优质博文:IT-BLOG-CN 线程的状态在Thread.State这个枚举类型中定义:共有6种状态,可以调用线程Thread种的getState()方法获取当前线程状态。 public enum State { /** * 新建状态(New): * 当用new操作符创建一个线程时&#…...

全球热门剪辑软件大搜罗
如果你要为你的视频进行配音那肯定离不开音频剪辑软件,现在有不少音频剪辑软件免费版本就可以实现我们并不复杂的音频剪辑操作。这次我就给你分享几款能提高剪辑效率的音频剪辑工具。 1.福晰音频剪辑 链接直达>>https://www.foxitsoftware.cn/audio-clip/ …...

swagger-bootstrap-ui页面空白,也没报错
回想起来,代码层面没有进行什么大的调整,增加了配置文件,application.yml中的 spring:profiles:active: sms # dev --> smsname: sms-server swagger配置未调整导致空白 修改profile 问题解决...

15.2 JDBC数据库编程2
15.2.1 数据库访问步骤 使用JDBC API连接和访问数据库,一般分为以下5个步骤: (1) 加载驱动程序 (2) 建立连接对象 (3) 创建语句对象 (4) 获得SQL语句的执行结果 (5) 关闭建立的对象,释放资源 下面将详细描述这些步骤 15.2.2 加载驱动程序 要使…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...

R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...

热烈祝贺埃文科技正式加入可信数据空间发展联盟
2025年4月29日,在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上,可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞,强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...

Vue3 PC端 UI组件库我更推荐Naive UI
一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用,前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率,还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库(Naive UI、Element …...
字符串哈希+KMP
P10468 兔子与兔子 #include<bits/stdc.h> using namespace std; typedef unsigned long long ull; const int N 1000010; ull a[N], pw[N]; int n; ull gethash(int l, int r){return a[r] - a[l - 1] * pw[r - l 1]; } signed main(){ios::sync_with_stdio(false), …...