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();

外觀模式的優缺點

優點

  • 簡化接口:將多個子系統的調用封裝成一個簡單接口,降低使用復雜度。
  • 降低耦合:客戶端與各子系統解耦,任何子系統的變化只需在外觀層做適配。
  • 隱藏內部復雜性:外觀模式屏蔽了子系統實現細節,使得系統更易于使用和維護。

缺點

  • 靈活性降低:外觀模式封裝了子系統的所有功能,可能限制了對某些細節的直接控制。
  • 外觀類可能過于龐大:當涉及的子系統很多時,外觀類的職責可能變得過于繁雜,需要合理設計職責分離。

總結

外觀模式通過為復雜系統提供一個統一而簡潔的接口,有效降低了客戶端與各子系統之間的耦合,使得系統調用更加直觀和易于維護。本文通過家庭影院、電子商務、網絡請求聚合、游戲初始化和跨平臺資源加載五個示例,展示了外觀模式在不同場景下的應用。希望這些實例能夠幫助你在實際項目中發現并利用外觀模式帶來的簡化接口和隱藏復雜性的優勢。

歡迎在評論區分享你的使用心得或疑問!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/70048.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/70048.shtml
英文地址,請注明出處:http://en.pswp.cn/web/70048.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【性能測試】如何理解“10個線程且10次循環“的請求和“100線程且1次循環“的請求

在性能測試中,我們常常會見到不同的并發配置:比如“10個線程且10次循環”與“100線程且1次循環”。乍一看,這兩個設置的總請求數都是100次,但它們對系統的壓力和測試場景卻截然不同。了解其中的區別,能幫助你更精準地模…

Spring Boot 實戰:輕松實現文件上傳與下載功能

目錄 一、引言 二、Spring Boot 文件上傳基礎 (一)依賴引入 (二)配置文件設置 (三)文件上傳接口編寫 (一)文件類型限制 (二)文件大小驗證 &#xff0…

【Golang】GC探秘/寫屏障是什么?

之前寫了 一篇【Golang】內存管理 ,有了很多的閱讀量,那么我就接著分享一下Golang的GC相關的學習。 由于Golang的GC機制一直在持續迭代,本文敘述的主要是Go1.9版本及以后的GC機制,該版本中Golang引入了 混合寫屏障大幅度地優化了S…

DeepSeek教unity------MessagePack-03

數據契約兼容性 你可以使用 [DataContract] 注解代替 [MessagePackObject]。如果類型用 DataContract 進行注解,可以使用 [DataMember] 注解代替 [Key],并使用 [IgnoreDataMember] 代替 [IgnoreMember]。 然后,[DataMember(Order int)] 的…

【對比】Pandas 和 Polars 的區別

Pandas vs Polars 對比表 特性PandasPolars開發語言Python(Cython 實現核心部分)Rust(高性能系統編程語言)性能較慢,尤其在大數據集上(內存占用高,計算效率低)極快,利用…

百度千帆平臺對接DeepSeek官方文檔

目錄 第一步:注冊賬號,開通千帆服務 第二步:創建應用,獲取調用秘鑰 第三步:調用模型,開啟AI對話 方式一:通過API直接調用 方式二:使用SDK快速調用 方式三:在千帆大模…

49. c++計時器

為了測試某段特定代碼的執行時間&#xff0c;體現代碼的性能&#xff0c;可以使用計時器對代碼段計時。下面使用std::chrono中的api編寫簡單案例&#xff1a; // // main.cpp // HelloWorld // // Created by on 2024/11/28. //#include <iostream> #include <vec…

Natural Language Processing NLP

NLP 清晰版本查看 Sentence segmentation (split)Tokenisation (split)Named entity recognition (combine) 概念主要內容典型方法Distributional Semantics&#xff08;分佈式語義&#xff09;&#xff08;分銷語義&#xff08;分佈式語義&#xff09;單詞的語義來自於它的…

Linux中線程創建,線程退出,線程接合

線程的簡單了解 之前我們了解過 task_struct 是用于描述進程的核心數據結構。它包含了一個進程的所有重要信息&#xff0c;并且在進程的生命周期內保持更新。我們想要獲取進程相關信息往往從這里得到。 在Linux中&#xff0c;線程的實現方式與進程類似&#xff0c;每個線程都…

HarmonyOS:使用List實現分組列表(包含粘性標題)

一、支持分組列表 在列表中支持數據的分組展示&#xff0c;可以使列表顯示結構清晰&#xff0c;查找方便&#xff0c;從而提高使用效率。分組列表在實際應用中十分常見&#xff0c;如下圖所示聯系人列表。 聯系人分組列表 在List組件中使用ListItemGroup對項目進行分組&#…

django上傳文件

1、settings.py配置 # 靜態文件配置 STATIC_URL /static/ STATICFILES_DIRS [BASE_DIR /static, ]上傳文件 # 定義一個視圖函數&#xff0c;該函數接收一個 request 參數 from django.shortcuts import render # 必備引入 import json from django.views.decorators.http i…

【前端知識】瀏覽器兼容方案polyfill

瀏覽器兼容方案polyfill 什么是 Polyfill&#xff1f;Polyfill 的作用Polyfill 的工作原理1. **特性檢測**2. **加載 Polyfill**3. **模擬實現** Polyfill 的常見場景Polyfill 的使用方式Polyfill 的優缺點優點缺點 常見的 Polyfill 庫總結 什么是 Polyfill&#xff1f; Polyf…

C#學習之DateTime 類

目錄 一、DateTime 類的常用方法和屬性的匯總表格 二、常用方法程序示例 1. 獲取當前本地時間 2. 獲取當前 UTC 時間 3. 格式化日期和時間 4. 獲取特定部分的時間 5. 獲取時間戳 6. 獲取時區信息 三、總結 一、DateTime 類的常用方法和屬性的匯總表格 在 C# 中&#x…

dedecms 開放重定向漏洞(附腳本)(CVE-2024-57241)

免責申明: 本文所描述的漏洞及其復現步驟僅供網絡安全研究與教育目的使用。任何人不得將本文提供的信息用于非法目的或未經授權的系統測試。作者不對任何由于使用本文信息而導致的直接或間接損害承擔責任。如涉及侵權,請及時與我們聯系,我們將盡快處理并刪除相關內容。 0x0…

如何選擇合適的超參數來訓練Bert和TextCNN模型?

選擇合適的超參數來訓練Bert和TextCNN模型是一個復雜但關鍵的過程&#xff0c;它會顯著影響模型的性能。以下是一些常見的超參數以及選擇它們的方法&#xff1a; 1. 與數據處理相關的超參數 最大序列長度&#xff08;max_length&#xff09; 含義&#xff1a;指輸入到Bert模…

AWS 前端自動化部署流程指南

本文詳細介紹從前端代碼開發到 AWS 自動化部署的完整流程。 一、流程概覽 1.1 部署流程圖 #mermaid-svg-nYg7k6L5IKVBjDtr {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-nYg7k6L5IKVBjDtr .error-icon{fill:#552…

Office word打開加載比較慢處理方法

1.添加safe參數 ,找到word啟動項,右擊word,選擇屬性 , 添加/safe , 應用并確定 2.取消加載項,點擊文件,點擊選項 ,點擊加載項,點擊轉到,取消所有勾選,確定。

大數據SQL調優專題——Spark執行原理

引入 在深入MapReduce中有提到&#xff0c;MapReduce雖然通過“分而治之”的思想&#xff0c;解決了海量數據的計算處理問題&#xff0c;但性能還是不太理想&#xff0c;這體現在兩個方面&#xff1a; 每個任務都有比較大的overhead&#xff0c;都需要預先把程序復制到各個 w…

MYSQL下載安裝及使用

MYSQL官網下載地址&#xff1a;https://downloads.mysql.com/archives/community/ 也可以直接在服務器執行指令下載&#xff0c;但是下載速度比較慢。還是自己下載好拷貝過來比較快。 wget https://dev.mysql.com/get/Downloads/mysql-5.7.38-linux-glibc2.12-x86_64.tar.gz 1…

CentOS 7.8 安裝MongoDB 7 副本集(Replica Set)

文章目錄 1 環境假設步驟1&#xff1a;在兩臺服務器上安裝MongoDB步驟2&#xff1a;配置副本集步驟3&#xff1a;初始化副本集步驟4&#xff1a;驗證副本集配置步驟5&#xff1a;設置安全性&#xff08;可選&#xff09;擴展配置示例&#xff1a;最佳實踐&#xff1a;仲裁節點步…