常見設計模式

概念

設計模式是怎么解決問題的一種方案

常見的設計模式

單例模式

概念:保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
應用:項目封裝個websocket用于大屏,redux,vuex都應用了單例模式的思想;現在很多第三方庫都是單例模式,多次引用只會使用同一個對象,如jquerylodashmoment
實現:先判斷實例存在與否,如果存在則直接返回,如果不存在就創建了再返回,這就確保了一個類只有一個實例對象

// 定義一個類
function Singleton(name) {this.name = name;this.instance = null;
}
// 原型擴展類的一個方法getName()
Singleton.prototype.getName = function() {console.log(this.name)
};
// 獲取類的實例
Singleton.getInstance = function(name) {if(!this.instance) {this.instance = new Singleton(name);}return this.instance
};// 獲取對象1
const a = Singleton.getInstance('a');
// 獲取對象2
const b = Singleton.getInstance('b');
// 進行比較
console.log(a === b);//true
工廠模式

概念:不暴露創建對象的具體邏輯,而是將邏輯封裝在一個函數中,那么這個函數就可以被視為一個工廠。
工廠模式根據抽象程度的不同可以分為:

  • 簡單工廠模式(Simple Factory):用一個工廠對象創建同一類對象類的實例.

    function Factory(career) {function User(career, work) {this.career = career this.work = work}let workswitch(career) {case 'coder':work =  ['寫代碼', '修Bug'] return new User(career, work)breakcase 'boss':work = ['喝茶', '開會', '審批文件']return new User(career, work)break}
    }
    let coder = new Factory('coder')
    console.log(coder)
    let boss = new Factory('boss')
    console.log(boss)
    
  • 工廠方法模式(Factory Method):工廠方法模式跟簡單工廠模式差不多,但是把具體的產品放到了工廠函數的prototype中.

    // 工廠方法
    function Factory(career){if(this instanceof Factory){var a = new this[career]();return a;}else{return new Factory(career);}
    }
    // 工廠方法函數的原型中設置所有對象的構造函數
    Factory.prototype={'coder': function(){this.careerName = '程序員'this.work = ['寫代碼', '修Bug'] },'hr': function(){this.careerName = 'HR'this.work = ['招聘', '員工信息管理']}
    }
    let coder = new Factory('coder')
    console.log(coder)
    let hr = new Factory('hr')
    console.log(hr)
    
  • 抽象工廠模式(Abstract Factory):簡單工廠模式和工廠方法模式都是直接生成實例,但是抽象工廠模式不同,抽象工廠模式并不直接生成實例, 而是用于對產品類簇的創建。

    通俗點來講就是:簡單工廠和工廠方法模式的工作是生產產品,那么抽象工廠模式的工作就是生產工廠的

    let CareerAbstractFactory = function(subType, superType) {// 判斷抽象工廠中是否有該抽象類if (typeof CareerAbstractFactory[superType] === 'function') {// 緩存類function F() {}// 繼承父類屬性和方法F.prototype = new CareerAbstractFactory[superType]()// 將子類的constructor指向父類subType.constructor = subType;// 子類原型繼承父類subType.prototype = new F()} else {throw new Error('抽象類不存在')}
    }
    //由于JavaScript中并沒有抽象類的概念,只能模擬,可以分成四部分:
    //用于創建抽象類的函數
    //抽象類
    //具體類
    //實例化具體類
    //上面代碼中CareerAbstractFactory就是一個抽象工廠方法,該方法在參數中傳遞子類和父類,在方法體內部實現了子類對父類的繼承
    

工廠模式適用場景如下:

  • 如果你不想讓某個子系統與較大的那個對象之間形成強耦合,而是想運行時從許多子系統中進行挑選的話,那么工廠模式是一個理想的選擇
  • 將new操作簡單封裝,遇到new的時候就應該考慮是否用工廠模式;
  • 需要依賴具體環境創建不同實例,這些實例都有相同的行為,這時候我們可以使用工廠模式,簡化實現的過程,同時也可以減少每種對象所需的代碼量,有利于消除對象間的耦合,提供更大的靈活性
策略模式

概念:定義一系列的算法,把它們一個個封裝起來,目的就是將算法的使用與算法的實現分離開來。
一個基于策略模式的程序至少由兩部分組成:
策略類(可變),策略類封裝了具體的算法,并負責具體的計算過程
環境類(不變),接受客戶的請求,隨后將請求委托給某一個策略類

var obj = {"A": function(salary) {return salary * 4;},"B" : function(salary) {return salary * 3;},"C" : function(salary) {return salary * 2;} 
};
var calculateBouns =function(level,salary) {return obj[level](salary);
};
console.log(calculateBouns('A',10000)); // 40000

策略模式的優點有如下:

  • 策略模式利用組合,委托等技術和思想,有效的避免很多if條件語句
  • 策略模式提供了開放-封閉原則,使代碼更容易理解和擴展
  • 策略模式中的代碼可以復用
代理模式

概念:給某一個對象提供一個代理對象,并由代理對象控制對原對象的引用。

ES6中,存在proxy構建函數能夠讓我們輕松使用代理模式:

const proxy = new Proxy(target, handler);

而按照功能來劃分,javascript代理模式常用的有:

  • 緩存代理:緩存代理可以為一些開銷大的運算結果提供暫時的存儲,在下次運算時,如果傳遞進來的參數跟之前一致,則可以直接返回前面存儲的運算結果。

    //緩存代理可以為一些開銷大的運算結果提供暫時的存儲,在下次運算時,如果傳遞進來的參數跟之前一致,則可以直接返回前面存儲的運算結果
    var proxyMult = (function () {var cache = {};return function () {var args = Array.prototype.join.call(arguments, ",");if (args in cache) {return cache[args];}return (cache[args] = mult.apply(this, arguments));};
    })();proxyMult(1, 2, 3, 4); // 輸出:24
    proxyMult(1, 2, 3, 4); // 輸出:24
    
  • 虛擬代理:虛擬代理把一些開銷很大的對象,延遲到真正需要它的時候才去創建。

    常見的就是圖片預加載功能:

    // 圖片本地對象,負責往頁面中創建一個img標簽,并且提供一個對外的setSrc接口
    let myImage = (function(){let imgNode = document.createElement( 'img' );document.body.appendChild( imgNode );return {//setSrc接口,外界調用這個接口,便可以給該img標簽設置src屬性setSrc: function( src ){imgNode.src = src;}}
    })();
    // 代理對象,負責圖片預加載功能
    let proxyImage = (function(){// 創建一個Image對象,用于加載需要設置的圖片let img = new Image;img.onload = function(){// 監聽到圖片加載完成后,給被代理的圖片本地對象設置src為加載完成后的圖片myImage.setSrc( this.src );}return {setSrc: function( src ){// 設置圖片時,在圖片未被真正加載好時,以這張圖作為loading,提示用戶圖片正在加載myImage.setSrc( 'https://img.zcool.cn/community/01deed576019060000018c1bd2352d.gif' );img.src = src;}}
    })();proxyImage.setSrc( 'https://xxx.jpg' );
    

    應用場景:

    現在的很多前端框架或者狀態管理框架都使用代理模式,用與監聽變量的變化。

    使用代理模式代理對象的訪問的方式,一般又被稱為攔截器,比如我們在項目中經常使用 Axios 的實例來進行 HTTP 的請求,使用攔截器 interceptor 可以提前對 請求前的數據 服務器返回的數據進行一些預處理。

    以及上述應用到的緩存代理和虛擬代理。

中介者模式

? 通過一個中介者對象,其他所有的相關對象都通過該中介者對象來通信,當其中的一個對象發生改變時,只需要通知中介者對象即可。

? 通過中介者模式可以解除對象與對象之間的緊耦合關系

裝飾者模式

? 在原有方法維持不變,在原有方法上再掛載其他方法來滿足現有需求。

觀察者模式

? 觀察者模式定義了對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴于它的對象都將得到通知,并自動更新。
? 觀察者模式屬于行為型模式,行為型模式關注的是對象之間的通訊,觀察者模式就是觀察者和被觀察者之間的通訊。
在這里插入圖片描述

// 被觀察者模式
class Subject {constructor() {this.observerList = [];}addObserver(observer) {this.observerList.push(observer);}removeObserver(observer) {const index = this.observerList.findIndex(o => o.name === observer.name);this.observerList.splice(index, 1);}notifyObservers(message) {const observers = this.observeList;observers.forEach(observer => observer.notified(message));}
}//觀察者
class Observer {constructor(name, subject) {this.name = name;if (subject) {subject.addObserver(this);}}notified(message) {console.log(this.name, 'got message', message);}
}
//使用
const subject = new Subject();
const observerA = new Observer('observerA', subject);
const observerB = new Observer('observerB');
subject.addObserver(observerB);
subject.notifyObservers('Hello from subject');
subject.removeObserver(observerA);
subject.notifyObservers('Hello again');
發布訂閱模式

? 發布-訂閱是一種消息范式,消息的發送者(稱為發布者)不會將消息直接發送給特定的接收者(稱為訂閱者)。而是將發布的消息分為不同的類別,無需了解哪些訂閱者(如果有的話)可能存在。
在這里插入圖片描述

// 發布訂閱中心
class PubSub {constructor() {this.messages = {};this.listeners = {};}// 添加發布者publish(type, content) {const existContent = this.messages[type];if (!existContent) {this.messages[type] = [];}this.messages[type].push(content);}// 添加訂閱者subscribe(type, cb) {const existListener = this.listeners[type];if (!existListener) {this.listeners[type] = [];}this.listeners[type].push(cb);}// 通知notify(type) {const messages = this.messages[type];const subscribers = this.listeners[type] || [];subscribers.forEach((cb, index) => cb(messages[index]));}
}//發布者代碼
class Publisher {constructor(name, context) {this.name = name;this.context = context;}publish(type, content) {this.context.publish(type, content);}
}//訂閱者代碼
class Subscriber {constructor(name, context) {this.name = name;this.context = context;}subscribe(type, cb) {this.context.subscribe(type, cb);}
}//使用代碼
const TYPE_A = 'music';
const TYPE_B = 'movie';
const TYPE_C = 'novel';const pubsub = new PubSub();const publisherA = new Publisher('publisherA', pubsub);
publisherA.publish(TYPE_A, 'we are young');
publisherA.publish(TYPE_B, 'the silicon valley');
const publisherB = new Publisher('publisherB', pubsub);
publisherB.publish(TYPE_A, 'stronger');
const publisherC = new Publisher('publisherC', pubsub);
publisherC.publish(TYPE_C, 'a brief history of time');const subscriberA = new Subscriber('subscriberA', pubsub);
subscriberA.subscribe(TYPE_A, res => {console.log('subscriberA received', res)
});
const subscriberB = new Subscriber('subscriberB', pubsub);
subscriberB.subscribe(TYPE_C, res => {console.log('subscriberB received', res)
});
const subscriberC = new Subscriber('subscriberC', pubsub);
subscriberC.subscribe(TYPE_B, res => {console.log('subscriberC received', res)
});pubsub.notify(TYPE_A);
pubsub.notify(TYPE_B);
pubsub.notify(TYPE_C);//發布者和訂閱者需要通過發布訂閱中心進行關聯,發布者的發布動作和訂閱者的訂閱動作相互獨立,無需關注對方,消息派發由發布訂閱中心負責。
發布訂閱、觀察者模式區別
  • 在觀察者模式中,觀察者是知道Subject的,Subject一直保持對觀察者進行記錄。然而,在發布訂閱模式中,發布者和訂閱者不知道對方的存在。它們只有通過消息代理進行通信。
  • 在發布訂閱模式中,組件是松散耦合的,正好和觀察者模式相反。
  • 觀察者模式大多數時候是同步的,比如當事件觸發,Subject就會去調用觀察者的方法。而發布-訂閱模式大多數時候是異步的(使用消息隊列)

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

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

相關文章

文獻閱讀:AnnoLLM: Making Large Language Models to Be Better Crowdsourced Annotators

文獻閱讀:AnnoLLM: Making Large Language Models to Be Better Crowdsourced Annotators 1. 文章簡介2. 方法介紹3. 實驗考察 1. 實驗結果2. 消解實驗3. Consistency & Stability 4. 結論 & 思考 文獻鏈接:https://arxiv.org/abs/2303.16854 …

Golang設計模式

Golang設計模式 Golang設計模式簡介Golang工廠設計模式Golang單例設計模式Golang抽象工廠設計模式Golang建造者模式 (Builder Pattern)Golang 原型模式(Prototype Pattern)Golang適配器模式Golang 橋接模式(Bridge Pattern)Golang裝飾器模式(Decorator …

j東h5st參數多局部ob加密(js_security_v3_0.1.4.js)加密分析

j東h5st參數多局部多次ob加密(js_security_v3_0.1.4.js) 大家好呀,我是你們的好兄弟,【星云horseAK】,今天的主題真的是千呼萬喚始出來,某東東的h5st參數,這個加密的js文件使用了obfuscator進行…

《Java-SE-第三十六章》之枚舉

前言 在你立足處深挖下去,就會有泉水涌出!別管蒙昧者們叫嚷:“下邊永遠是地獄!” 博客主頁:KC老衲愛尼姑的博客主頁 博主的github,平常所寫代碼皆在于此 共勉:talk is cheap, show me the code 作者是爪哇島的新手,水平很有限&…

Linux 日志管理

Linux 日志管理 一.Linux 下的日志服務簡介 1.1 CentOS5 之前的版本 centos5 之前的版本使用系統和內核日志分離的格式記錄日志 syslogd:該服務專門用于記錄系統日志(system application logs) klogd: 該服務專門用于記錄內核日志(linux kernel logs) centos5 之前事件的記錄格…

Redis_Geospatial(基于位置信息的應用)

12.Geospatial 12.1 簡介 基于位置信息服務(Location-Based Service,LBS)的應用。Redis3.2版本后增加了對GEO類型的支持。主要來維護元素的經緯度。redis基于這種類型,提供了經緯度設置、查詢、范圍查詢、距離查詢、經緯度hash等一些相關操作 12.2 GEO底層結構 …

war和war exploded

war和war exploded的區別 war模式&#xff1a;將WEB工程以包的形式上傳到服務器 &#xff1b; war exploded模式&#xff1a;將WEB工程以當前文件夾的位置關系上傳到服務器&#xff1b;>> war包是自己打包生成的&#xff0c;如pom文件中<packaging>war</packag…

使用 Visual Studio Code 調試 CMake 腳本

之前被引入到 Visual Studio 中的 CMake 調試器&#xff0c;現已在 Visual Studio Code 中可用。 也就是說&#xff0c;現在你可以通過在 VS Code 中安裝 CMake 工具擴展&#xff0c;來調試你的 CMakeLists.txt 腳本了。是不是很棒? 背景知識 Visual C 開發團隊和 CMake 的維…

Flutter源碼分析筆記:Widget類源碼分析

Flutter源碼分析筆記 Widget類源碼分析 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at: https://jclee95.blog.csdn.netEmail: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_28550263/article/details/132259681 【介紹】&#x…

TestNG和Junit5測試框架梳理

一、testNG 1. testNG優勢 注解驅動&#xff1a; TestNG 使用注解來標識測試方法、測試類和配置方法&#xff0c;使得測試更具可讀性。 并行執行&#xff1a; TestNG 支持多線程并行執行測試&#xff0c;可以加速測試套件的執行。 豐富的配置&#xff1a; 可以通過 XML 配置文…

Qt下載安裝及配置教程

進入qt中文網站&#xff1a;https://www.qt.io/zh-cn/ 下載開源版 往下滑&#xff0c;下載Qt在線安裝程序 它已經檢測出我的是windows系統&#xff0c;直接點擊download就好。如果是其它的系統&#xff0c;需要找到對應自己系統的安裝包。 然后跟網速有關&#xff0c;等…

Gitlab CI/CD筆記-第三天-使用主機docker in docker 進行構建并push鏡像。

一、啥叫docker in docker 就是允許的鏡像里頭有一個docker,但這個docekr鏡像只有docker的cli和/var/lib/docker.sock的套接字&#xff0c;沒有允許build.然后里頭又運行了一個docker&#xff0c;這個docker有build的能力&#xff0c;此時構建時就是里頭的docker使用外部的dock…

登錄驗證碼實現

Hutool代碼改造 Hutool 有參考文檔&#xff1b;很多工具類&#xff1b;把一些功能都封裝好&#xff1b;都不用你自己去寫&#xff1b;直接調用它的工具類 它這里會詳細告訴你引入方式Hutool <dependency><groupId>cn.hutool</groupId><artifactId>hu…

STM32F429IGT6使用CubeMX配置SPI通信(W25Q256芯片)

1、硬件電路 需要系統性的看一下W25Q256芯片手冊 2、設置RCC&#xff0c;選擇高速外部時鐘HSE,時鐘設置為180MHz 3、配置SPI 4、生成工程配置 5、相關代碼 #define sFLASH_ID 0XEF4019 // W25Q256#define SPI_FLASH_PageSize 256 #define SPI_FLASH_PerWritePageSize 256#def…

《雷達像智能識別對抗研究進展》閱讀記錄

&#xff08;1&#xff09;引言 ? 神經網絡通常存在魯棒性缺陷&#xff0c;易受到對抗攻擊的威脅。攻擊者可以隱蔽的誘導雷達智能目標識別做出錯誤預測&#xff0c;如&#xff1a; ? a圖是自行車&#xff0c;加上對抗擾動后神經網絡就會將其識別為挖掘機。 &#xff08;2&a…

【Quarkus技術系列】打造基于Quarkus的云原生微服務框架實踐(1)

前提介紹 本系列文章主要講解如何基于Quarkus技術搭建和開發"專為Kubernetes而優化的Java微服務框架"的入門和實踐&#xff0c;你將會學習到如何搭建Quarkus微服務腳環境及腳手架&#xff0c;開發Quarkus的端點服務&#xff0c;系統和應用層級的配置介紹與Quarkus的…

單芯片3路CC管理的VR轉接器解決方案

VR眼鏡即VR頭顯&#xff0c;也稱虛擬現實頭戴式顯示設備&#xff0c;隨著元宇宙概念的傳播&#xff0c;VR眼鏡的熱度一直只增不減&#xff0c;但是頭戴設備的續航一直被人詬病&#xff0c;如果增大電池就會讓頭顯變得笨重影響體驗&#xff0c;所以目前最佳的解決方案還是使用VR…

C# BeginInvoke 加 EndInvoke實現異步操作

1、定義一個委托 delegate long MyDel(int first, int second); 2、 需異步操作的函數 static int sum(int x,int y) {Console.WriteLine("InSide Sum1");Thread.Sleep(1000);Console.WriteLine("InSide Sum2");return x y;} 3、回調方法…

[HDLBits] Exams/m2014 q3

Consider the function f shown in the Karnaugh map below. Implement this function. d is dont-care, which means you may choose to output whatever value is convenient. //empty