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

JavaScript设计模式 -- 外观模式

在实际开发中,往往会遇到多个子系统协同工作时,直接操作各个子系统不仅接口繁琐,还容易导致客户端与内部实现紧密耦合。**外观模式(Facade Pattern)**通过为多个子系统提供一个统一的高层接口,将复杂性隐藏在内部,从而降低耦合,提高代码的可维护性与易用性。

本文将详细介绍外观模式的基本概念、结构和优缺点,并通过多个示例展示如何在不同场景下运用外观模式。

外观模式简介

外观模式属于结构型设计模式,其核心思想是为多个复杂子系统提供一个统一的接口(外观),从而让客户端无需了解内部实现细节即可调用系统功能。外观模式不仅能简化调用流程,还能使系统的内部变化对客户端透明,便于后续扩展和维护。


外观模式的结构与特点

主要角色:

  • 外观(Facade)
    对外提供一个统一的接口,封装多个子系统的功能调用。

  • 子系统(Subsystem)
    完成具体的业务逻辑,各自拥有独立的接口和实现。

  • 客户端(Client)
    只需通过外观接口与系统交互,无需关心子系统内部的复杂细节。

特点:

  • 简化接口:将复杂的操作组合成简单的方法调用。
  • 降低耦合:客户端只依赖外观接口,而不直接与各个子系统耦合。
  • 隐藏复杂性:将系统内部的实现细节封装在外观类中,对外部屏蔽。

多方面示例详解

下面通过多个示例,展示如何利用外观模式解决不同场景下的复杂性问题。

示例 1:家庭影院系统

在家庭影院中,通常需要协调音响、投影仪、灯光、蓝光播放器等多个设备。直接控制这些设备非常繁琐,使用外观模式可以为家庭影院提供一键启动和关闭的简单接口。

// 子系统:音响
class Amplifier {on() {console.log('音响开启');}off() {console.log('音响关闭');}setVolume(level) {console.log(`音响音量设置为 ${level}`);}
}// 子系统:投影仪
class Projector {on() {console.log('投影仪开启');}off() {console.log('投影仪关闭');}setInput(input) {console.log(`投影仪输入源设置为 ${input}`);}
}// 子系统:灯光
class TheaterLights {dim(level) {console.log(`灯光调暗到 ${level}%`);}on() {console.log('灯光开启');}
}// 子系统:蓝光播放器
class BluRayPlayer {on() {console.log('蓝光播放器开启');}off() {console.log('蓝光播放器关闭');}play(movie) {console.log(`正在播放电影:《${movie}》`);}
}// 外观类:家庭影院外观
class HomeTheaterFacade {constructor(amp, projector, lights, bluRay) {this.amp = amp;this.projector = projector;this.lights = lights;this.bluRay = bluRay;}watchMovie(movie) {console.log('准备观看电影...');this.lights.dim(10);this.projector.on();this.projector.setInput('蓝光播放器');this.amp.on();this.amp.setVolume(5);this.bluRay.on();this.bluRay.play(movie);}endMovie() {console.log('结束观看电影...');this.lights.on();this.projector.off();this.amp.off();this.bluRay.off();}
}// 客户端调用
const amplifier = new Amplifier();
const projector = new Projector();
const lights = new TheaterLights();
const bluRayPlayer = new BluRayPlayer();const homeTheater = new HomeTheaterFacade(amplifier, projector, lights, bluRayPlayer);
homeTheater.watchMovie('阿凡达');
homeTheater.endMovie();

示例 2:电子商务系统统一接口

在电子商务平台中,下单流程可能涉及订单创建、支付处理、物流安排和通知发送等多个子系统。使用外观模式,可以将这些流程封装为一个简单的 placeOrder 接口。

// 子系统:订单处理
class OrderService {createOrder(orderDetails) {console.log(`订单已创建:${JSON.stringify(orderDetails)}`);return 'ORDER123';}
}// 子系统:支付系统
class PaymentService {processPayment(orderId, amount) {console.log(`订单 ${orderId} 支付金额 ${amount} 元成功`);}
}// 子系统:物流系统
class ShippingService {arrangeShipping(orderId) {console.log(`订单 ${orderId} 发货成功`);}
}// 子系统:通知系统
class NotificationService {sendNotification(message) {console.log(`发送通知:${message}`);}
}// 外观类:购物流程外观
class ShoppingFacade {constructor(orderService, paymentService, shippingService, notificationService) {this.orderService = orderService;this.paymentService = paymentService;this.shippingService = shippingService;this.notificationService = notificationService;}placeOrder(orderDetails, amount) {console.log('开始下单流程...');const orderId = this.orderService.createOrder(orderDetails);this.paymentService.processPayment(orderId, amount);this.shippingService.arrangeShipping(orderId);this.notificationService.sendNotification(`订单 ${orderId} 已完成处理`);}
}// 客户端调用
const orderService = new OrderService();
const paymentService = new PaymentService();
const shippingService = new ShippingService();
const notificationService = new NotificationService();const shopping = new ShoppingFacade(orderService, paymentService, shippingService, notificationService);
shopping.placeOrder({ item: '笔记本电脑', quantity: 1 }, 8000);

示例 3:网络请求聚合接口

在一个大型 Web 应用中,客户端可能需要从多个 API 接口获取数据(例如用户信息、订单信息、统计数据等)。通过外观模式,可以设计一个统一的 API 聚合层,对外暴露简洁的接口,而内部调用各个子 API。

// 子系统:用户服务
class UserService {fetchUser(userId) {console.log(`获取用户 ${userId} 信息...`);return { id: userId, name: '张三' };}
}// 子系统:订单服务
class OrderServiceAPI {fetchOrders(userId) {console.log(`获取用户 ${userId} 的订单...`);return [{ orderId: 'ORDER123', item: '手机' }];}
}// 子系统:统计服务
class StatsService {fetchStats(userId) {console.log(`获取用户 ${userId} 的统计数据...`);return { totalOrders: 5, totalSpent: 1200 };}
}// 外观类:API 聚合器
class APIServiceFacade {constructor(userService, orderService, statsService) {this.userService = userService;this.orderService = orderService;this.statsService = statsService;}getUserDashboard(userId) {const user = this.userService.fetchUser(userId);const orders = this.orderService.fetchOrders(userId);const stats = this.statsService.fetchStats(userId);return {user,orders,stats};}
}// 客户端调用
const userServiceInstance = new UserService();
const orderServiceInstance = new OrderServiceAPI();
const statsServiceInstance = new StatsService();const apiFacade = new APIServiceFacade(userServiceInstance, orderServiceInstance, statsServiceInstance);
const dashboard = apiFacade.getUserDashboard(101);
console.log('用户仪表盘数据:', dashboard);

示例 4:游戏初始化外观

在游戏开发中,启动一个游戏往往需要初始化多个子系统,如图形引擎、音频系统、输入管理等。通过外观模式,可以为游戏引擎提供一个统一的初始化接口,简化启动流程。

// 子系统:图形引擎
class GraphicsEngine {init() {console.log('图形引擎初始化完成');}
}// 子系统:音频系统
class AudioSystem {init() {console.log('音频系统初始化完成');}
}// 子系统:输入管理
class InputManager {init() {console.log('输入管理初始化完成');}
}// 外观类:游戏引擎外观
class GameEngineFacade {constructor(graphics, audio, input) {this.graphics = graphics;this.audio = audio;this.input = input;}initializeGame() {console.log('游戏启动中...');this.graphics.init();this.audio.init();this.input.init();console.log('游戏初始化完毕');}
}// 客户端调用
const graphicsEngine = new GraphicsEngine();
const audioSystem = new AudioSystem();
const inputManager = new InputManager();const gameEngine = new GameEngineFacade(graphicsEngine, audioSystem, inputManager);
gameEngine.initializeGame();

示例 5:跨平台资源加载

在移动开发或跨平台项目中,不同平台可能需要加载不同的资源或配置。利用外观模式可以创建一个资源加载外观,根据当前平台选择合适的加载器,从而对外提供统一的加载接口。

// 子系统:Android 资源加载器
class AndroidResourceLoader {loadResources() {console.log('加载 Android 平台资源...');}
}// 子系统:iOS 资源加载器
class IOSResourceLoader {loadResources() {console.log('加载 iOS 平台资源...');}
}// 外观类:跨平台资源加载器
class ResourceFacade {constructor(platform) {// 假设 platform 值为 'android' 或 'ios'this.loader = platform === 'android'? new AndroidResourceLoader(): new IOSResourceLoader();}load() {this.loader.loadResources();}
}// 客户端调用
const currentPlatform = 'ios'; // 模拟当前平台为 iOS
const resourceLoader = new ResourceFacade(currentPlatform);
resourceLoader.load();

外观模式的优缺点

优点

  • 简化接口:将多个子系统的调用封装成一个简单接口,降低使用复杂度。
  • 降低耦合:客户端与各子系统解耦,任何子系统的变化只需在外观层做适配。
  • 隐藏内部复杂性:外观模式屏蔽了子系统实现细节,使得系统更易于使用和维护。

缺点

  • 灵活性降低:外观模式封装了子系统的所有功能,可能限制了对某些细节的直接控制。
  • 外观类可能过于庞大:当涉及的子系统很多时,外观类的职责可能变得过于繁杂,需要合理设计职责分离。

总结

外观模式通过为复杂系统提供一个统一而简洁的接口,有效降低了客户端与各子系统之间的耦合,使得系统调用更加直观和易于维护。本文通过家庭影院、电子商务、网络请求聚合、游戏初始化和跨平台资源加载五个示例,展示了外观模式在不同场景下的应用。希望这些实例能够帮助你在实际项目中发现并利用外观模式带来的简化接口和隐藏复杂性的优势。

欢迎在评论区分享你的使用心得或疑问!

相关文章:

JavaScript设计模式 -- 外观模式

在实际开发中,往往会遇到多个子系统协同工作时,直接操作各个子系统不仅接口繁琐,还容易导致客户端与内部实现紧密耦合。**外观模式(Facade Pattern)**通过为多个子系统提供一个统一的高层接口,将复杂性隐藏…...

百达翡丽(Patek Philippe):瑞士制表的巅峰之作(中英双语)

百达翡丽(Patek Philippe):瑞士制表的巅峰之作 在钟表界,百达翡丽(Patek Philippe) 一直被誉为“世界三大名表”之一,并且常被认为是其中的至高存在。一句“没人能真正拥有一枚百达翡丽&#x…...

阿里云一键部署DeepSeek-V3、DeepSeek-R1模型

目录 支持的模型列表 模型部署 模型调用 WebUI使用 在线调试 API调用 关于成本 FAQ 点击部署后服务长时间等待 服务部署成功后,调用API返回404 请求太长导致EAS网关超时 部署完成后,如何在EAS的在线调试页面调试 模型部署之后没有“联网搜索…...

分享一款AI绘画图片展示和分享的小程序

🎨奇绘图册 【开源】一款帮AI绘画爱好者维护绘图作品的小程序 查看Demo 反馈 github 文章目录 前言一、奇绘图册是什么?二、项目全景三、预览体验3.1 截图示例3.2 在线体验 四、功能介绍4.1 小程序4.2 服务端 五、安装部署5.1 快速开始~~5.2 手动部…...

【练习】【双指针】力扣热题100 283. 移动零

题目 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 请注意 ,必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0] 示例 2: 输入: nums [0] 输出…...

QT 互斥锁

一、概述 1、在多线程编程中,为了防止多个线程同时访问共享资源而导致的不确定性和错误,经常会使用互斥锁(Mutex)进行保护。 2、QMutex是Qt提供的一个互斥锁类,用于确保在同一时间只有一个线程访问共享资源。 3、QM…...

什么是算法的空间复杂度和时间复杂度,分别怎么衡量。

1. 时间复杂度 时间复杂度衡量的是算法运行时间与输入规模之间的关系。它通常用大O记号(Big O Notation)表示,例如 O(1)、O(n)、O(n2) 等。 衡量方法: 常数时间复杂度 O(1):无论输入规模如何,算法的执行时…...

VMware Workstation 17.0 Pro创建虚拟机并安装Ubuntu22.04与ubuntu20.04(双版本同时存在)《包含小问题总结》

目录 一、创建虚拟机 二、下载安装22.04 三、一些配置问题总结(小屏,网络,复制贴贴等) 1、网络问题 2、sudo apt install net-tools出现无法定为软件包 3、小屏与ubuntu虚拟机与windows系统之间复制粘贴 4、安装终端:Termi…...

Windows 10 ARM工控主板CAN总线实时性能测试

在常规的Windows系统中支持CAN总线应用,需要外接CAN总线适配器,通常为USB转CAN模块或PCI接口CAN卡。实时性本身是CAN总线的显著特性之一,但由于Windows并非实时操作系统,应用程序容易受到系统CPU负载影响,导致调度周期…...

如何在不依赖函数调用功能的情况下结合工具与大型语言模型

当大型语言模型(LLM)原生不支持函数调用功能时,如何实现智能工具调度?本文通过自然语言解析结构化输出控制的方法来实现。 GitHub代码地址 核心实现步骤 定义工具函数 使用tool装饰器声明可调用工具: from langcha…...

【Linux AnolisOS】关于Docker的一系列问题。尤其是拉取东西时的网络问题,镜像源问题。

AnolisOS 8中使用Docker部署(全)_anolis安装docker-CSDN博客 从在虚拟机安装龙蜥到安装docker上面这篇文章写的很清晰了,我重点讲述我解决文章里面问题一些的方法。 问题1: docker: Get https://registry-1.docker.io/v2/: net/h…...

【Elasticsearch】Mapping概述

以下是Elasticsearch中提到的关于Mapping的各模块概述: --- 1.Dynamic mapping(动态映射) 动态映射是指Elasticsearch在索引文档时,自动检测字段类型并创建字段映射的过程。当你首次索引一个文档时,Elasticsearch会根…...

GPT-4o悄然升级:能力与个性双突破,AI竞技场再掀波澜

在大模型竞技场中,GPT-4o悄悄发布了全新版本,凭借其卓越的多项能力,迅速超越了DeepSeek-R1,成功登上并列第一的位置。这次更新不仅在数学(第6名)上有所突破,还在创意写作、编程、指令遵循、长文…...

如何选择合适的超参数来训练Bert和TextCNN模型?

选择合适的超参数来训练Bert和TextCNN模型是一个复杂但关键的过程,它会显著影响模型的性能。以下是一些常见的超参数以及选择它们的方法: 1. 与数据处理相关的超参数 最大序列长度(max_length) 含义:指输入到Bert模…...

C# SpinLock 类 使用详解

总目录 前言 SpinLock 是 C# 中一种轻量级的自旋锁,属于 System.Threading 命名空间,专为极短时间锁竞争的高性能场景设计。它通过忙等待(自旋)而非阻塞线程来减少上下文切换开销,适用于锁持有时间极短(如…...

【linux】在 Linux 上部署 DeepSeek-r1:32/70b:解决下载中断问题

【linux】在 Linux 上部署 DeepSeek-r1:32/70b:解决下载中断问题 【承接商业广告,如需商业合作请+v17740568442】 文章目录 【linux】在 Linux 上部署 DeepSeek-r1:32/70b:解决下载中断问题问题描述:解决方法方法一:手动中断并重启下载方法二:使用 Bash 脚本自动化下载在…...

机器学习所需要的数学知识【01】

总览 导数 行列式 偏导数 概理论 凸优化-梯度下降 kkt条件...

4.【线性代数】——矩阵的LU分解

四 矩阵的LU分解 1. AB的逆矩阵2. 转置矩阵3. ALU3.1 2x2矩阵3.2 3x3矩阵3.3 nxn的矩阵分解的次数? 1. AB的逆矩阵 { ( A B ) ( B − 1 A − 1 ) I ( B − 1 A − 1 ) ( A B ) I ⇒ ( A B ) − 1 B − 1 A − 1 \begin{cases} (AB)(B^{-1}A^{-1}) I\\ (B^{-1}A^…...

【清晰教程】本地部署DeepSeek-r1模型

【清晰教程】通过Docker为本地DeepSeek-r1部署WebUI界面-CSDN博客 目录 Ollama 安装Ollama DeepSeek-r1模型 安装DeepSeek-r1模型 Ollama Ollama 是一个开源工具,专注于简化大型语言模型(LLMs)的本地部署和管理。它允许用户在本地计算机…...

Spring Cloud工程搭建

目录 工程搭建 搭建父子工程 创建父工程 Spring Cloud版本 创建子项目-订单服务 声明项⽬依赖 和 项⽬构建插件 创建子项目-商品服务 声明项⽬依赖 和 项⽬构建插件 工程搭建 因为拆分成了微服务,所以要拆分出多个项目,但是IDEA只能一个窗口有一…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...

XCTF-web-easyupload

试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...

离线语音识别方案分析

随着人工智能技术的不断发展&#xff0c;语音识别技术也得到了广泛的应用&#xff0c;从智能家居到车载系统&#xff0c;语音识别正在改变我们与设备的交互方式。尤其是离线语音识别&#xff0c;由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力&#xff0c;广…...

Android屏幕刷新率与FPS(Frames Per Second) 120hz

Android屏幕刷新率与FPS(Frames Per Second) 120hz 屏幕刷新率是屏幕每秒钟刷新显示内容的次数&#xff0c;单位是赫兹&#xff08;Hz&#xff09;。 60Hz 屏幕&#xff1a;每秒刷新 60 次&#xff0c;每次刷新间隔约 16.67ms 90Hz 屏幕&#xff1a;每秒刷新 90 次&#xff0c;…...