JS利用中介模式開(kāi)發(fā)全局控制器
中介模式
中介模式定義了一個(gè)單獨(dú)的(中介)對(duì)象,來(lái)封裝一組對(duì)象之間的交互。將這組對(duì)象之間的交互委派給與中介對(duì)象交互,來(lái)避免對(duì)象之間的直接交互。
在實(shí)際的項(xiàng)目中,程序由許多對(duì)象組成,對(duì)象間的交流錯(cuò)綜復(fù)雜。

隨著應(yīng)用程序的規(guī)模增大,對(duì)象越來(lái)愈多,他們之間的關(guān)系也越來(lái)復(fù)雜。對(duì)象間很容易出現(xiàn)相互引用而導(dǎo)致程序無(wú)法運(yùn)行。同時(shí)開(kāi)發(fā)者需要改變或者刪除某一個(gè)對(duì)象時(shí)候,需要查找并且改造所有引用到它的對(duì)象。這樣一來(lái),改造的成本會(huì)變的非常高。
但中介者模式可以讓各個(gè)對(duì)象之間得以解耦。

之前的場(chǎng)景下,如果 A 發(fā)生了改變,開(kāi)發(fā)者需要修改 B、D、E、F 4 個(gè)對(duì)象,而通過(guò)中介者模式,我們只需要修改中介者這一個(gè)對(duì)象即可。
中介者的好處是簡(jiǎn)化了對(duì)象之間的交互,壞處則是中介類(lèi)有可能會(huì)變成大而復(fù)雜的“上帝類(lèi)”(God Class)。所以,在使用中介者模式時(shí)候,設(shè)計(jì)上一定要非??酥?。
實(shí)際使用
在前端項(xiàng)目開(kāi)發(fā)的過(guò)程中,有很多業(yè)務(wù)無(wú)關(guān)的功能,但這些功能會(huì)散落在各個(gè)業(yè)務(wù)中,難以管理,我們利用中介者模式的思想來(lái)構(gòu)建一個(gè)的控制器。
基礎(chǔ)控制器
// axios 請(qǐng)求工具
import axios from "axios";
// mitt 微型發(fā)布訂閱工具
import mitt from "mitt";
const createApi = ($controller) => {
const api = axios.create({
baseURL: "/api",
timeout: 10000,
});
api.interceptors.request.use(() => {
// 可以通過(guò) $controller.tmpCache 緩存一些數(shù)據(jù)
});
return api;
};
const createBus = () => mitt();
class Controller {
// 臨時(shí)緩存,也可以添加更復(fù)雜的緩存
tmpCache: Record<string, any> = {};
// 事件總線
bus = createBus();
constructor() {
this.api = createApi(this);
}
static instance: Controller | null = null;
static getInstance() {
if (!Controller.instance) {
Controller.instance = new Controller();
}
return Controller.instance;
}
}
export default Controller;此時(shí)控制器中有一個(gè)極簡(jiǎn)的緩存 tmpCache,發(fā)布訂閱工具。以及服務(wù)端請(qǐng)求方法 api。開(kāi)發(fā)者只需要導(dǎo)入 Controller 類(lèi)即可使用。如果后續(xù)需要更加復(fù)雜的緩存或者改造 api(如切換為 fetch)請(qǐng)求方法。開(kāi)發(fā)者可以很快的進(jìn)行替換。而無(wú)需改造對(duì)應(yīng)文件。
import axios from "redaxios";
const createApi = ($controller) => {
const api = axios.create({
baseURL: "/api",
});
const getOld = api.get;
api.get = (...params) => {
// 如果出發(fā)緩存直接返回
// if (xxx) {
// return $controller.tmpCache
// }
return getOld(...params);
};
return api;
};添加用戶(hù)類(lèi)
登錄的用戶(hù)信息以及對(duì)應(yīng)操作可以說(shuō)是對(duì)象交互的核心,將其放入控制器中。
class User {
readonly $controller: Controller;
user: User | null = null;
constructor($controller: Controller) {
this.$controller = $controller;
}
getData(key?: string) {
return key ? this.user[key] : this.user;
}
// 登錄
login(params) {
$controller.api.post("/login", params).then((response) => {
// 處理授權(quán)以及 user 信息
});
}
// 退出
logout() {
$controller.api.post("/logout").then(() => {
// 清理對(duì)應(yīng)數(shù)據(jù)
$controller.tmpCache = {};
this.user = null;
});
}
// 設(shè)置用戶(hù)配置
setSetting(params) {
return $controller.api.post("/setting", params).then(() => {
this.user.setting = params;
});
}
}
class Controller {
constructor() {
this.api = createApi(this);
this.user = new User(this);
}
logout() {
this.user.logout();
}
}此時(shí)控制器已經(jīng)具備前端開(kāi)發(fā)中“大部分”功能了,如數(shù)據(jù)緩存,數(shù)據(jù)請(qǐng)求,登錄用戶(hù)信息處理等。大部分情況下,中介類(lèi)也基本夠用了。至于其他的工具類(lèi),除非 80% 的業(yè)務(wù)都會(huì)用到,否則不建議添加到此類(lèi)中去。但對(duì)于這些工具做一層簡(jiǎn)單的封裝也是必要的(考慮后續(xù)成本)。
添加業(yè)務(wù)類(lèi)
此時(shí)控制器有了用戶(hù)信息,多種緩存,請(qǐng)求方法等。我們可以通過(guò)注入來(lái)實(shí)現(xiàn)其他業(yè)務(wù)。
class BasicService {
constructor($controller) {
this.api = $controller.api;
this.bus = $controller.bus;
}
}
class OrderService extends BasicService {
static xxxKey = "xxxx";
constructor($controller) {
super($controller);
// 使用全局緩存
this.cache = $controller.xxxCache;
// 或者構(gòu)建一個(gè)新的 cache 掛載到 controller 上
this.cache = $controller.getCreatedCache("order", XXXCache);
}
getOrders = async () => {
if (this.cache.has(OrderService.xxxKey)) {
return this.cache.get(OrderService.xxxKey);
}
let orders = await this.api.get("xxxxx");
// 業(yè)務(wù)處理,包括其他的業(yè)務(wù)開(kāi)發(fā)
order = order.map();
this.cache.set(OrderService.xxxKey, orders);
// 可以發(fā)送全局事件
this.bus.emit("getOrdersSuccess", orders);
return orders;
};
clear() {
// 如果構(gòu)建一個(gè)新的緩存對(duì)象,直接 clear 即可 this.cache.clear();
this.cache.delete(OrderService.xxxKey);
}
}
class Controller {
// 臨時(shí)緩存,也可以添加更復(fù)雜的緩存
tmpCache: Record<string, any> = {};
// 事件總線
bus = createBus();
services: Record<string, BasicService> = {};
getService(serviceCls) {
// 同一個(gè)類(lèi) name 為相同的名稱(chēng)
const { name } = serviceCls;
if (!this.services[name]) {
this.services[name] = new serviceCls(Controller.instance);
}
return this.services[name];
}
}開(kāi)發(fā)者可以在具體的代碼中這樣使用。
import Controller from "../../controller"; import OrderService from "./order-service"; // A 頁(yè)面 const orderService = Controller.getInstance().getService(OrderService); const priceService = Controller.getInstance().getService(PriceService); // B 頁(yè)面都會(huì)使用同一個(gè)服務(wù) const orderService = Controller.getInstance().getService(OrderService);
有同學(xué)可能會(huì)奇怪,為什么 Controller 不能直接導(dǎo)入或者自動(dòng)注入 OrderService 呢。這樣使用的原因是往往 Controller 和 BasicService 在基礎(chǔ)類(lèi)中,具體的業(yè)務(wù)類(lèi)分布在各個(gè)業(yè)務(wù)代碼中。
這里沒(méi)有使用任何依賴(lài)注入的框架,原因是因?yàn)槿绻肓艘蕾?lài)注入會(huì)大大增加復(fù)雜度。同時(shí)前端的業(yè)務(wù)都是分散的,沒(méi)有必要都放在一起。在組件需要使用到服務(wù)候才將其裝載起來(lái)。
以上就是JS利用中介模式開(kāi)發(fā)全局控制器的詳細(xì)內(nèi)容,更多關(guān)于JS中介模式全局控制器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript setTimeout與setTimeinterval使用案例詳解
這篇文章主要介紹了JavaScript setTimeout與setTimeinterval使用案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
JS監(jiān)聽(tīng)組合按鍵思路及實(shí)現(xiàn)過(guò)程
這篇文章主要介紹了JS監(jiān)聽(tīng)組合按鍵思路及實(shí)現(xiàn)過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
JS中video標(biāo)簽自動(dòng)播放音視頻并繪制波形圖效果
html中的<video>標(biāo)簽可以用來(lái)播放常見(jiàn)的音視頻格式,支持的格式包括:MP3、Ogg、WAV、AAC、MP4、WebM、AVI等,當(dāng)然支持的格式也和瀏覽器和操作系統(tǒng)有關(guān),這篇文章主要介紹了video標(biāo)簽自動(dòng)播放音視頻并繪制波形圖,需要的朋友可以參考下2023-09-09
利用JS解決ie6不支持max-width,max-height問(wèn)題的方法
本篇文章主要介紹了利用JS解決ie6不支持max-width,max-height問(wèn)題的方法。需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2014-01-01
表單提交前觸發(fā)函數(shù)返回true表單才會(huì)提交
這篇文章主要介紹了表單提交前觸發(fā)函數(shù)當(dāng)返回true表單才會(huì)提交的具體實(shí)現(xiàn),需要的朋友可以參考下2014-03-03
Wordpress ThickBox 點(diǎn)擊圖片顯示下一張圖的修改方法
Wordpress自帶的ThickBox特效中,單擊圖片會(huì)調(diào)用 tb_remove 以關(guān)閉特效窗口,現(xiàn)將修改其動(dòng)作為顯示下一張圖。2010-12-12

