創建型模式:單例模式

来源:https://www.cnblogs.com/liebrother/archive/2019/01/14/10269746.html
-Advertisement-
Play Games

個人博客原文: "創建型模式:單例模式" 簡介 姓名 :單例模式 英文名 :Singleton Pattern 價值觀 :我的生活我主宰(只允許自己實例化,不願意被其他對象實例化) 個人介紹 : Ensure a class has only one instance, and provide a ...


個人博客原文:
創建型模式:單例模式

月

簡介

姓名:單例模式

英文名:Singleton Pattern

價值觀:我的生活我主宰(只允許自己實例化,不願意被其他對象實例化)

個人介紹

Ensure a class has only one instance, and provide a global point of access to it.(確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例。)
(來自《設計模式之禪》)

這裡的關註點有 3 個,分別是:

  1. 只有一個實例
  2. 自行實例化(也就是主動實例化)
  3. 向整個系統提供這個實例

你要的故事

我們腦洞大開來用一個故事講解一番。

小明家裡有一輛小汽車,具體什麼牌子就不知道了,咱也不關註,反正他家裡就這麼一輛車,小明比較懶,只要一齣門都會開車,例如去旅游、去學校、去聚會都會開車去。下麵模擬小明出去的場景。

class Car {
    public void run() {
        System.out.println("走。。。。");
    }
}


class XiaoMing {
    public Car travel() {
        System.out.println("小明去旅游");
        Car car = new Car();
        car.run();
        return car;
    }

    public Car goToSchool() {
        System.out.println("小明去學校");
        Car car = new Car();
        car.run();
        return car;
    }

    public Car getTogether() {
        System.out.println("小明參加聚會");
        Car car = new Car();
        car.run();
        return car;
    }
}

public class SingletonErrorTest {

    public static void main(String[] args) {
        XiaoMing xiaoMing = new XiaoMing();
        Car car1 = xiaoMing.travel();
        Car car2 = xiaoMing.goToSchool();
        Car car3 = xiaoMing.getTogether();
    }

}

上面小汽車只有一個方法,就是走。小明去旅游、去學校、參加聚會都開著他唯一的一輛汽車車去。是不是有人有疑問?為什麼每個方法都返回 Car 對象?其實只是想在下麵做一次檢查,檢查小明去旅游、去學校和參加聚會的車是不是同一輛。下麵是檢查代碼:

System.out.println("car1 == car2 ? " + (car1 == car2));
System.out.println("car2 == car3 ? " + (car2 == car3));

最終結果是啥?很明顯是 2 個 false。小明去旅游、去學校和參加聚會的車都不相同,小明不是只有 1 輛車?關鍵在於 Car car = new Car(); 這一句代碼,其實這一句是創建一輛車,每次都重新創建一輛。那應該怎麼實現小明只有一輛車呢?這時候就引入了單例模式

上面我們說到了單例模式需要具備的 3 個點:只有 1 個實例,很顯然,上面的代碼不止 1 個實例,而是有 3 個 Car 實例;自行實例化,Car 本身沒有主動實例化,而是在小明需要用到的時候才實例化;向整個系統提供這個實例,因為 Car 沒有主動實例化,所以它沒法向外部暴露提供自己出來。

我們的代碼完全不符合單例模式的要求。我們要通過修改,使之符合單例模式的 3 個要點。首先需要實現的是第 2 點,把 Car 實例化從小明轉為 Car 本身,如下代碼

class Car1{

    private static Car1 car1 = new Car1();

    private Car1() {

    }

    public void run(){
        System.out.println("走。。。。");
    }
}

上面代碼使用 private 修飾構造方法,使得 Car1 不能被其他使用方實例化,通過 Car1 car1 = new Car1(); 主動實例化自己。

接下來再實現第 3 點,向整個系統暴露這個實例,也就是暴露它自己。每個使用方都調用 Car1.getInstance() 方法來獲取實例。

class Car1{

    private static Car1 car1 = new Car1();

    public static Car1 getInstance() {
        return car1;
    }
    
    private Car1() {

    }

    public void run(){
        System.out.println("走。。。。");
    }
}

上面代碼就實現了單例模式的 2 和 3 要點,第 1 要點要怎麼實現呢?告訴你,不用實現,只要滿足了 2 和 3 要點就可以,第 1 要點是用來檢驗是否是單例模式的好思路。我們檢驗一下

class Car1{

    private static Car1 car1 = new Car1();

    public static Car1 getInstance() {
        return car1;
    }

    private Car1() {

    }

    public void run(){
        System.out.println("走。。。。");
    }
}

class XiaoMing1 {
    public Car1 travel() {
        System.out.println("小明去旅游");
        Car1 car = Car1.getInstance();
        car.run();
        return car;
    }

    public Car1 goToSchool() {
        System.out.println("小明去學校");
        Car1 car = Car1.getInstance();
        car.run();
        return car;
    }

    public Car1 getTogether() {
        System.out.println("小明參加聚會");
        Car1 car = Car1.getInstance();
        car.run();
        return car;
    }
}

public class SingletonRightHungryTest {

    public static void main(String[] args) {
        XiaoMing1 xiaoMing1 = new XiaoMing1();
        Car1 car1 = xiaoMing1.travel();
        Car1 car2 = xiaoMing1.goToSchool();
        Car1 car3 = xiaoMing1.getTogether();

        System.out.println("car1 == car2 ? " + (car1 == car2));
        System.out.println("car2 == car3 ? " + (car2 == car3));
    }

}

上面代碼最後兩行列印出來的結果是啥?是我們想要的:2 個 true。說明小明這幾次外出開的車都是同一輛。這是最簡單的單例模式的實現方式,我們經常稱作餓漢式單例模式。為什麼起這麼古怪的名字呢?其實和對應的懶漢式單例模式有關,這是 2 個實現方式的差別,餓漢式單例模式實現方式在類載入到記憶體的時候,就創建好對象了,而懶漢式則是在第一次使用的時候才創建對象,也就是把創建對象的時機從載入延遲到第一次使用,所以才有懶餓之分。

下麵我們來看怎麼實現懶漢式單例模式。先描述一下場景:小明還沒有汽車,他也不知道什麼時候要買汽車,突然某一天,他想去旅游,覺得是時候買輛車了,然後他就買車去旅游了,旅游回來又開車去學校和參加聚會。

class Car2{

    private static Car2 car2;

    public static synchronized Car2 getInstance() {
        if (null == car2) {
            System.out.println("買車啦。。。");
            car2 = new Car2();
        }
        return car2;
    }

    private Car2() {

    }

    public void run(){
        System.out.println("走。。。。");
    }
}

class XiaoMing2
{
    public Car2 travel() {
        System.out.println("小明去旅游");
        Car2 car = Car2.getInstance();
        car.run();
        return car;
    }

    public Car2 goToSchool() {
        System.out.println("小明去學校");
        Car2 car = Car2.getInstance();
        car.run();
        return car;
    }

    public Car2 getTogether() {
        System.out.println("小明參加聚會");
        Car2 car = Car2.getInstance();
        car.run();
        return car;
    }
}

public class SingletonRightLazyTest {

    public static void main(String[] args) {
        XiaoMing2 xiaoMing2 = new XiaoMing2();
        Car2 car1 = xiaoMing2.travel();
        Car2 car2 = xiaoMing2.goToSchool();
        Car2 car3 = xiaoMing2.getTogether();

        System.out.println("car1 == car2 ? " + (car1 == car2));
        System.out.println("car2 == car3 ? " + (car2 == car3));
    }

}

小明去旅游
買車啦。。。
走。。。。
小明去學校
走。。。。
小明參加聚會
走。。。。
car1 == car2 ? true
car2 == car3 ? true

上面附帶了列印出來的結果,小明要去旅游的時候,才去買車。這就是懶漢式單例模式的實現方式。

要註意懶漢式單例模式有個很關鍵的一點就是 getInstance() 方法帶上了 synchronized,這個是為什麼呢?

首先得瞭解關鍵字 synchronized 的作用是什麼:用於修飾執行方法同步,也就是說多線程併發的情況下,在一個時間點,只允許一個線程執行這個方法。

不加上這個會有什麼結果?在多線程併發情況下,如果有 2 個線程同時執行到 if(null == car2),那麼都判斷為 true,這時 2 個線程都會執行 car2 = new Car2(),這樣子就不是單例了。

總結

單例模式可以說是設計模式中最簡單的一個,也是在工作中很多場景下經常用到的,比如:項目的配置文件載入、各種工具類等等。我們對於單例模式最重要的一點就是要考慮多線程併發,沒有考慮這點就容易引發單例對象不單例的情況。而單例給我們帶來最大的好處就是節約記憶體

上面實現的兩種方法是單例模式中最最最簡單的 2 種實現,相信也是用得最多的實現方式。網上有不少網友分享了單例模式的很多種實現方法,大家也可以去瞭解,在瞭解之前務必已經搞懂文中這 2 種最簡單的實現方式,不然會頭暈的。

參考資料:《大話設計模式》、《Java設計模式》、《設計模式之禪》、《研磨設計模式》、《Head First 設計模式》

希望文章對您有所幫助,設計模式系列會持續更新,感興趣的同學可以關註公眾號,第一時間獲取文章推送閱讀,也可以一起交流,交個朋友。

公眾號之設計模式系列文章

公眾號


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • vue-resource+iview上傳文件取消上傳 子組件: 父組件調用: ...
  • JavaScript 事件 HTML 事件是發生在 HTML 元素上的事情。 當在 HTML 頁面中使用 JavaScript 時, JavaScript 可以觸發這些事件。 1.HTML 事件 HTML 事件可以是瀏覽器行為,也可以是用戶行為。 以下是 HTML 事件的實例: HTML 頁面完成加 ...
  • LayaBox案例分享 小程式開篇(3) 不知不覺已經是上線小程式基礎篇的最後一篇了,今天我會把源碼發到本文的底部,有需要的可以拿去練手。 大家可以體驗一下,請掃碼: 這個頁面我們主要用到的知識有;佈局依然是WEUI;數據解析插件WxParse,下麵會額外分享一下WxParse的兩種用法;微信小程式 ...
  • 問題 父級元素不能被子元素內容撐開的解決辦法,父級元素沒有高度的解決辦法。 今天在寫網頁時遇到如下圖問題,解決問題後自己做個隨筆,希望幫到更多的學前端的童鞋! 問題圖片 問題描述 最外層的父級元素不能自適應高度-不能隨對象撐開沒有高度 當在對象內的盒子使用了float後,導致對象本身不能被撐開自適應 ...
  • 此插件是一位外國人寫的,官網API地址:https://photo-sphere-viewer.js.org/#methods 我只是記錄下我在使用此插件時用到的方法和相關屬性,以防以後忘記 1.按要求在頁面中引入文件後,使用以下方式調用,其它配置 jq選中的元素最後返回的是一個document,不 ...
  • 一.組件模板和樣式 類似於頁面,自定義組件擁有自己的 wxml 和模板 wxss 樣式。 1.組件模板 組件的寫法和頁面的寫法相同,組件模板與組件數據結合後生成的數節點, 將被插入到組件的引用位置。在組件模板中提供一個<slot> 節點,用於承載組件 引用時候提供的子節點。 例如: <view cl ...
  • 函數是由事件驅動的或者當它被調用時執行的可重覆使用的代碼塊。 實例1: 效果:通過點擊按鈕“點我”,會彈出"Hello World!"的提示框! 1.JavaScript 基本函數語法 函數就是包裹在花括弧中的代碼塊,前面使用了關鍵詞 function: 當調用該函數時,會執行函數內的代碼。 可以在 ...
  • 今天,棧長分享下 Zookeeper 的集群安裝及配置。 下載 下載地址:http://zookeeper.apache.org/ 下載過程就不說了,我們下載了最新的 。 安裝 1、上傳安裝包 把下載的最新的包(如:zookeeper 3.4.11.tar.gz)上傳到伺服器,上傳的方式也不多說了。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...