設計模式大冒險第四關:單例模式,如何成為你的「唯一」

2020-12-10 達文西的酒杯

這一篇文章是關於設計模式大冒險系列的第四篇文章,這一系列的每一篇文章我都希望能夠通過通俗易懂的語言描述或者日常生活中的小例子來幫助大家理解好每一種設計模式。今天這篇文章來跟大家一起學習一下單例模式。相信讀完這篇文章之後,你肯定會有所收穫的。

關於單例模式,這應該是設計模式中最簡單的一種了。大家如果學習過設計模式,可能很多設計模式長時間不用就忘記了,但是對於單例模式來說,你肯定不會忘記。因為它的理論知識比較簡單,實踐起來也很方便

但是,你真的會正確的使用單例模式嗎?你知道單例模式在什麼情況下使用是合適的,什麼情況下使用會造成很多麻煩嗎?還是你只是把它當做一個全局變量去使用,只是因為這樣開發很方便,不用寫很多的代碼。今天這篇文章我們就來一起好好學習一下單例模式。讓我們開始吧。

單例模式的介紹

首先我們先來看一下單例模式的定義是什麼。所謂的單例模式,就是指對於一個具體的類來說,它有且只有一個實例,這個類負責創建唯一的實例,並且對外提供一個全局的訪問接口

單例模式的UML類圖可以用下圖表示:

UML那麼我們為什麼要使用單例模式呢?舉一個生活中的場景,在平時你過馬路的時候,給你信號提示你能不能穿過馬路的交通信號燈是不是只有一個?因為在這種情況下,如果同時有兩個信號燈的話,你是不知道該不該在此時穿過馬路的

所以類比到我們的軟體開發中,也是這麼一個道理。在一個系統中,某種用途的實例會存在唯一的一個。這個實例可能用來保存應用中的一些狀態,或者執行某些任務。比如在前端開發中,我們常常會使用一些應用的狀態管理庫,比如Vuex或者Redux。那麼在我們的應用中,對於管理狀態的實例也只能有一個,如果有多個的話就會讓應用的狀態出現問題,從而導致應用發生一些錯誤。

單例模式的實現

接下來我們來看一下單例模式是如何實現的。通過上面的UML類圖,我們可以知道,對於一個類來說,我們需要一個靜態變量來保存實例的引用,還需要對外提供一個獲取實例的靜態方法。如果使用 ES6的類的語法來實現的話,可以簡單的用下面的代碼來表示:

classSingleton{// 類的靜態屬性static instance = null;// 類的靜態方法static getInstance() {if (this.instance === null) {this.instance = new Singleton(); }returnthis.instance; }}const a = Singleton.getInstance();const b = Singleton.getInstance();console.log(a === b); // true上面的代碼還是比較簡單的,相信大家看一下就知道怎麼實現了。需要注意的一點是,在類的靜態方法中,this指的是類,而不是實例

下面我們再使用函數的方式來實現一次:

const Singleton = (function() {let instance;// 初始化單例對象的方法functioninitInstance() {return {}; }return { getInstance() {if (instance === null) { instance = initInstance(); }return instance; }, };})();const a = Singleton.getInstance();const b = Singleton.getInstance();console.log(a === b); // true上面這兩種方法的實現都是差不多的,你可以根據自己的喜好選擇不同的實現方式。

多線程環境中的單例模式

作為Web前端開發者來說,因為我們使用的開發語言基本上是JavaScript,又因為JavaScript是一種單線程語言,所以我們一般不會遇到在多線程環境中使用單例模式會遇到的一些問題。

那麼我們如果在多線程的環境中使用單例模式需要注意什麼呢?首先在單例還沒有初始化的時候,如果有多個線程訪問創建單例模式的代碼,在沒有做額外處理的情況下,就有可能會創建多個單例

當然也有解決的方法,一種方法就是我們在類初始化的時候就把單例生成了,這樣以後通過獲取單例的接口獲取到的都是最開始生成的那個單例。但是這樣就失去了延時初始化單例的好處。如果單例的初始化需要花費的資源或者時間比較少,這種方法是可以的。反之,這樣做有就有一些浪費了。因為可能在整個應用的運行過程中,這個單例一次也沒有被使用過

另一種方式就是在創建單例的時候需要加鎖,保證同時只能有一個線程在創建單例。這樣的話我們就保證了創建的單例是唯一的。當然具體的操作還跟實現單例模式選擇的語言有關係,這裡就不在深入討論了。

單例模式的適用場景和優勢

單例模式適合用在這樣的場景中:系統中需要一個唯一的對象去控制、管理和分享系統的狀態,或者執行某一個特定的任務又或是實現某一個具體的功能。在我們的前端開發中,最常見的就是應用的狀態管理對象,比如 VuexRedux。又或者是列印日誌的對象,或者是某一個功能插件等等。總之單例模式在我們平時的開發中還是比較常見的。

那麼單例模式的優勢有哪些呢?下面簡單列舉了一些:

全局只有一個實例,提供統一的訪問與修改,保證狀態功能的一致性簡單、方便,容易實現延遲的初始化,只有在需要的時候才去初始化對象單例模式的劣勢

雖然單例模式的優勢很突出,但是它的缺點可是一點都不少,甚至有些開發者覺得它是反模式的。所以我們使用單例模式的時候一定要好好思考一下,確定是不是必須要使用單例模式。因為單例模式的不恰當使用會給整個應用的測試,開發和維護帶來很大的困難。我們接下來就來看看單例模式有哪些缺點。

單例模式的濫用會造成跟全局變量一樣的一些問題

比如會增加代碼的耦合性,因為單例模式全局都是可以訪問到的,那麼我們就很有可能在很多個地方使用這個唯一的對象,這樣也就造成了代碼的耦合。

因為程序中使用到這個單例對象的地方都可以對全局的狀態進行修改,所以一旦程序在這裡出現了問題,你可能要在很多個地方進行排查,這就增加了調試和排查問題的難度。

單例模式給測試帶來了很多麻煩

為什麼說單例模式對測試來說是一個災難呢?因為如果代碼中使用了單例,那麼我們需要在進行代碼測試的時候,提前把單例初始化好。這導致了我們不能夠在單例沒有初始化好的時候對代碼進行單元測試。

而且因為單例模式產生的實例只有一個,這就導致了對相同代碼進行多次測試的時候容易出現問題,因為實例的狀態很可能在上一次測試的時候發生了改變,從而導致了下一次測試的失敗或者異常。

所以說單例模式增加了測試的難度與複雜度,增加了測試代碼的工作量。

單例模式違背了軟體設計的單一職責原則

這個比較容易理解,因為一般情況下,對於一個類來說它只負責這個類的實例具有什麼功能;但是對於單例模式來說,單例模式的類還需要負責只能夠產生一個實例。這違背了軟體設計的單一職責原則,類應該只負責其實例的具體功能,而不應該對類產生的實例個數負責。

但是對於這個缺點來說,大家可能會有不同的看法。顯而易見的是這樣做確實更加方便,設計實現上也相對簡單一些。

單例模式隱藏了它所需要的依賴

對於一般的類來說,如果我們的類依賴了其它的類,一般情況下,我們可以通過類的構造函數將依賴的類顯式的表示出來。這樣我們在初始化具體的類的實例的時候就知道這個類需要那些依賴。

但是對於單例模式來說,它把它的依賴封裝在內部,對於外部的使用者來說它是一個黑盒。使用者並不知道初始化這個單例需要那些依賴,所以很容易在初始化單例的時候把單例所需要的依賴忘記掉,進而導致單例初始化失敗。

有時就算我們知道了初始化單例需要那些依賴,但是這些依賴也許是有先後的順序的。我們也很容易在導入和使用依賴的時候把順序搞錯了,從而導致單例的初始化出現問題。

單例模式的總結

從上面的內容我們已經知道單例模式是一把雙刃劍,所以你在使用的時候一定要考慮清楚。先從場景的需求上考慮,是不是一定要使用單例模式才能夠解決當前的問題,有沒有其它的方案。如果一定要使用單例模式的話,如何規範單例模式的使用,如何在程序的開發,可維護性,可拓展性以及測試的簡易性上做好平衡,是一個值得考慮的問題

文章到這裡就結束了,如果大家有什麼問題和疑問歡迎大家在文章下面留言,或者在這裡提出來。也歡迎大家關注我的公眾號關山不難越,獲取更多關於設計模式講解的內容。

下面是這一系列的其它的文章,也歡迎大家閱讀,希望大家都能夠掌握好這些設計模式的使用場景和解決的方法。如果這篇文章對你有所幫助,那就點個讚,分享一下吧~

設計模式大冒險第三關:工廠模式,封裝和解耦你的代碼設計模式大冒險第二關:裝飾者模式,煎餅果子的主場設計模式大冒險第一關:觀察者模式參考連結:

Use your singletons wiselySingletonSingleton Pattern Pitfalls單例模式單例模式

相關焦點

  • 大話設計模式之愛你一萬年:第二章 創建型模式:單例模式::我的女朋友只有你一個:2.單例模式的實現-懶漢模式
    大話設計模式之愛你一萬年:第一章 設計模式基本概念:大話設計模式之愛你一萬年:第一章 設計模式基本概念:2.GoF的23種設計模式的分類和功能大話設計模式之愛你一萬年:第一章 設計模式基本概念:3.設計模式的六大原則大話設計模式之愛你一萬年:第二章 創建型模式:單例模式::我的女朋友只有你一個:1.單例模式的基本概念
  • 單例模式絕對沒有想像的那麼簡單!不服來看!
    一、前言單例模式(Singleton Pattern)是 Java 中最常用的設計模式之一,同時也是面試的重災區。有些人可能覺的單例模式很簡單,沒有什麼難的。其實不然,因為牽扯到線程安全的問題,所以單例模式絕對能體現出你的功底。不信接著往下看。
  • 《爐石傳說》冒險模式迦拉克隆的覺醒第四章怎麼過 冒險模式迦拉...
    導 讀 《爐石傳說》巨龍年最後的冒險模式「迦拉克隆的覺醒」已經正式上線,玩家可以通過通關冒險模式來獲得全新的卡牌
  • 刀塔傳奇挑戰模式第三章第四關通關攻略
    刀塔傳奇挑戰模式第三章第四關通關攻略,隨著關卡的開放,難度也在一步步上升。從操作難度到英雄裝備要求都有一定的提升,雖然難了一點,但還是能過關的。下面就為大家具體說明下每關的打法。
  • 面試必問的「單例模式」究竟是什麼鬼?有哪些優缺點和實現方式
    很多面試python初級崗位的同學,有大概率會被問到單例模式,而且一般都會現場讓你寫一個單例。可是有很多同學可能不是很理解,甚至專門把代碼背下來,說實話,有點不靠譜。01官方定義單例是一種設計模式,應用該模式的類只會生成一個實例。
  • 黑石山火雲蔽日 卡拉贊狂歡未眠 盤點《爐石傳說》冒險模式五大設計
    這一點在當年甚至成為不少老牌手詬病的對象,因為是經典包的設計,這些年也沒有過大的變動。可以說,「冒險模式」早期線性副本式的設計,是遵從魔獸既有設定的原汁原味的一條明線,在之後「加基森」、「拉斯塔哈的大亂鬥」等新式設計中,為了展現爐石宇宙中的原創精神,設計上也更放飛了許多。設計二:殘局破除「冒險模式」對於一些玩家來說,有的時候像打牌,有的時候卻像解殘局——它可能是一種情境的再現或投射。
  • 爐石傳說迦拉克隆的覺醒冒險模式性價比分析
    爐石傳說迦拉克隆的覺醒冒險模式性價比分析 價格還是和以往一樣128全部解鎖,本次冒險模式有邪惡和正義兩條線,關卡有24個,只有怪盜線第一章是免費的。 該冒險模式是單線的,所以玩家也不用去擔心打不完什麼的,沒以前那麼複雜,而且全部通關後可獲得35張新卡牌,後續小編會更新新卡。
  • python項目實踐分享:利用模塊實現單例模式
    在GOF的23種設計模式中,單例是最常使用的模式,通過單例模式可以保證系統中一個類只有一個實例而且該實例易於被外界訪問,從而方便對實例個數的控制並節約系統資源。每當大家想要實現一個名為XxxManger的類時,往往意味著這是一個單例。
  • 爐石傳說:冒險模式《迦拉克隆的覺醒》第四章英雄模式攻略
    今天爐石傳說更新了迦拉克隆的覺醒第四章的冒險模式,英雄模式依然很快通過,下面分享下大致攻略。第四章開啟怪盜軍團線喬治和卡爾主動英雄技能(2費):使你的武器獲得+1攻擊力(喬治)神奇的雷諾被動英雄技能:在你釋放一個法術後,隨機釋放一個同費用的法術注意事項:雷諾原來真的是一條龍,在英雄模式下,他的卡組是一套宇宙法,而且還不會像故事模式那樣變出聯盟和部落的酋長來,通過我們改良後的無限火球法就輕鬆過關了。
  • 黑石山火雲蔽日卡拉贊狂歡未眠盤點《爐石傳說》冒險模式五大設計
    這一點在當年甚至成為不少老牌手詬病的對象,因為是經典包的設計,這些年也沒有過大的變動。然而,隨著2014年「納克薩瑪斯的詛咒」以分區分時上線的形式,重新展現《魔獸世界》中宏偉的團隊副本之後,這樣的聲音就幾乎不復存在了。
  • 《戰歌競技場》冒險模式怎麼玩 冒險模式全關卡速通攻略
    導 讀 戰歌競技場冒險模式是有很多不同的關卡存在的,每一關的難度都不相同,但是有的特別的難,比如說5-5
  • 《爐石傳說》冒險模式迦拉克隆的覺醒怎麼玩 冒險模式迦拉克隆的...
    爐石傳說在近日上線了全新冒險模式迦拉克隆的覺醒,很多玩家都想知道爐石傳說巨龍降臨冒險模式怎麼玩?下面我就給大家帶來詳細介紹,一起來了解下吧! 爐石傳說迦拉克隆的覺醒冒險... 爐石傳說迦拉克隆的覺醒怎麼玩?
  • 饑荒冒險模式最終關:如何殺死王座上的麥斯威爾?
    哈嘍大家好,我是你們的老朋友阿誠,今天阿誠要跟大家分享一個小秘密,那就是在《饑荒單機版》的遊戲中,一直被許多人忽略的冒險模式恰恰就是單機世界裡面的主線劇情。這段劇情會告訴來到饑荒世界的玩家,這個世界的造物者麥斯威爾不過是一個囚徒而已真正的幕後黑手其實另有他人。
  • 爐石傳說:新版本「冒險模式」值得購買嗎?這三點告訴你答案!
    這裡是最"真實"的爐石趣談,大家好,我是小髒髒,如果你喜歡本文章不要忘記點一波關注分享給更多的朋友哦~一而衰,再而竭,原定於5月10日左右開啟的新版本冒險模式如今成為了未知數,這無疑讓我們充沛的熱情被澆滅了一半。
  • 《爐石傳說》樹木學家德克斯特冒險模式通關攻略 樹木學家德克斯特...
    導 讀 爐石傳說砰砰計劃冒險模式上線,樹木學家德克斯特是謎題實驗室清場篇的第一個BOSS,一共有八個關卡。那麼爐石傳說樹木學家德克斯特怎麼過?
  • 《太鼓達人 神秘大冒險》歌曲數量及冒險模式
    BNEI公司的著名的音樂遊戲《太鼓達人》系列新作確定將在2016年6月16日在3DS平臺上發售,新作名為《太鼓達人:神秘大冒險》(太鼓の達人 ドコドン!
  • 爐石傳說兩大冒險模式打折,你想要的全都有!
    一、卡牌包/奧術之塵第一個要說的就是兩大打折冒險模式之中最適合標準玩家的達拉然大劫案。眾所周知,爐石傳說是一個卡牌遊戲,卡牌遊戲和卡牌包肯定是密不可分的。達拉然大劫案這個模式可以給你15包暗影崛起卡包,另有一包金色經典卡包。從分塵量來算一包金色經典就相當於四包左右的普通卡包,簡直不要太賺。
  • 《命運2》單刷無暇模式攻略 異端深淵1-5關通關技巧
    《命運2》無暇模式下單刷異端深淵這個副本有一定難度,但在熟悉地圖等機制後相對來說也是比較容易的。下面為大家帶來「EDeNn」分享的《命運2》單刷無暇模式異端深淵詳細攻略。  .有人說卡BUG會容易的多,經過我多次測試卡BUG,第二關或者第三關就會暴斃,因為卡成功以後閃身有很大的機率閃身不會隱身,導致獵人最大的優勢沒了,很容易死根據我的配裝跑圖不卡BUG,兩次全過
  • 張瑞敏:於VUCA時代創造引領商業模式——人單合一
    如何在VUCA時代實現引領?張瑞敏在9月20日第四屆人單合一模式國際論壇發表的主旨演講中,系統的從方向、路徑和目標為我們勾畫了一幅黑海中的生存的新地圖。 商業生態系統理論之父詹姆斯·F·穆爾說海爾走的是一條對全人類的未來至關重要的道路。
  • 《爐石傳說》冒險模式攻略 模式卡組推薦
    導 讀 爐石傳說冒險模式開啟已經有一段時間了,現在能通關的玩家也基本打的差不多了。