我的設計模式之旅 ② 單例模式

来源:https://www.cnblogs.com/linxiaoxu/archive/2022/09/09/16672173.html
-Advertisement-
Play Games

一個菜鳥的設計模式之旅,使用 Golang 實現。本節實現單例模式。三個工作者需要各自找到電梯搭乘!電梯只有一個! ...


一個菜鳥的設計模式之旅,文章可能會有不對的地方,懇請大佬指出錯誤。

編程旅途是漫長遙遠的,在不同時刻有不同的感悟,本文會一直更新下去。

程式介紹

image-20220908221824615

本程式實現了單例模式,三個工作者需要各自找到電梯搭乘!電梯只有一個!

PS C:\Users\小能喵喵喵\Desktop\設計模式\單例模式> go run .
向海寧 正在搭乘電梯!
田海彬 正在搭乘電梯!

程式代碼

singleton.go

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

type people string

type elevator struct {
	passengers map[people]bool
}

var (
	Elevator    *elevator
	initialized uint32
	mu          sync.Mutex
)

// 餓漢式單例
func init() {
	initialized = 1
	Elevator = &elevator{make(map[people]bool)}
}

// 乘客進電梯
func (p people) intoElevator() {
	e := ElevatorGetInstance()
	e.passengers[p] = true
}

// 乘客出電梯
func (p people) outElevator() {
	e := ElevatorGetInstance()
	delete(e.passengers, p)
}

// 乘客按下電梯樓層按鈕
func (p people) pressButton() {
	e := ElevatorGetInstance()
	e.start()
}

// 電梯開始運作
func (e *elevator) start() {
	for k := range e.passengers {
		fmt.Println(k, "正在搭乘電梯!")
	}
}

// 乘客想知道電梯在哪,單例模式
func ElevatorGetInstance() *elevator {
	if atomic.LoadUint32(&initialized) == 1 {
		return Elevator
	}
	mu.Lock()
	defer mu.Unlock()
	// ^ 懶漢式單例
	if Elevator == nil {
		Elevator = &elevator{make(map[people]bool)}
		atomic.StoreUint32(&initialized, 1)
	}
	return Elevator
}

main.go

package main

// 單例模式

// by 小能喵喵喵 2022年9月8日

func main() {
	var workerA, workerB, workerC people = "陳冰", "向海寧", "田海彬"
	workerA.intoElevator()
	workerB.intoElevator()
	workerC.intoElevator()
	// workerA 發現自己電梯坐錯了
	workerA.outElevator()
	// workerB 按下了電梯按鈕
	workerB.pressButton()
}

Console

PS C:\Users\小能喵喵喵\Desktop\設計模式\單例模式> go run .
向海寧 正在搭乘電梯!
田海彬 正在搭乘電梯!

補充C#單線程單例實現

class Singleton
{
    private static Singleton instance;
    private Singleton() //使用 private 欄位外界無法使用new手動創建實例,只能通過靜態方法創建
    {    
    }
    public static Singleton GetInstance()
    {
        if(instance == null)
        {
            instance = new Singleton();
        }
        return instance
    }
}

所有類都有構造方法,不編碼則系統預設生成空的構造方法,若有顯示定義的構造方法,預設的構造方法就會失效。

思考總結

什麼是單例模式

單例模式(Singleton Pattern)屬於創建型模式,它提供了一種創建對象的最佳方式。

單例模式:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。

這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。(由於Golang不支持靜態方法、靜態欄位,所以使用純函數替代面向對象中的靜態方法,即上面程式的ElevatorGetInstance()函數)

職責角度看,實例化與否不應該由使用方判斷,而是應該由自己來判斷。將實例化判斷過程遷移到GetInstance()函數。

在C#中的單例模式,Singleton類封裝它的唯一實例,這樣它可以嚴格地控制客戶怎樣訪問它以及何時訪問它。簡單地說就是對唯一實例的受控訪問。當然在golang的實現中,我們也可以更改ElevatorGetInstance() 函數添加條件和參數實現受控訪問。

註意:

  • 單例類只能有一個實例。
  • 單例類必須自己創建自己的唯一實例。
  • 單例類必須給所有其他對象提供這一實例。

主要解決:一個全局使用的類頻繁地創建與銷毀。

何時使用:當您想控制實例數目,節省系統資源的時候。

如何解決:判斷系統是否已經有這個單例,如果有則返回,如果沒有則創建。

關鍵代碼:構造函數是私有的。(c#)

應用實例:

  • 1、一個班級只有一個班主任。
  • 2、Windows 是多進程多線程的,在操作一個文件的時候,就不可避免地出現多個進程或線程同時操作一個文件的現象,所以所有文件的處理必須通過唯一的實例來進行。
  • 3、一些設備管理器常常設計為單例模式,比如一個電腦有兩台印表機,在輸出的時候就要處理不能兩台印表機列印同一個文件。

優點:

  • 1、在記憶體里只有一個實例,減少了記憶體的開銷,尤其是頻繁的創建和銷毀實例(比如管理學院首頁頁面緩存)。
  • 2、避免對資源的多重占用(比如寫文件操作)。

缺點:沒有介面,不能繼承,與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來實例化。

使用場景:

  • 1、要求生產唯一序列號。
  • 2、WEB 中的計數器,不用每次刷新都在資料庫裡加一次,用單例先緩存起來。
  • 3、創建的一個對象需要消耗的資源過多,比如 I/O 與資料庫的連接等。

註意事項:getInstance() 方法中需要使用同步鎖防止多線程同時進入造成 instance 被多次實例化。

雙重”鎖定“

原子操作,是併發編程中”最小的且不可並行化“的操作。本案例中使用原子操作配合互斥鎖實現了非常高效的單例模式。互斥鎖的代價比普通整數的原子讀寫高很多,所以在性能敏感的地方添加一個initialized標誌位,通過原子檢測標誌位狀態降低互斥鎖的次數來提高性能。(也可以使用實例來進行判斷,在實例未被創建的時候再加鎖處理)

在互斥鎖之後還要判斷實例是否存在,是因為當多線程的時候,當一個線程處理完退出解鎖,另一個在排隊等候的線程進入後如果沒有實例的判斷,那麼會再生成一遍實例,沒有達到單例的目的。

靜態初始化

C# 提供了靜態初始化的方法,這種方法可以解決多線程環境下不安全的原因。

C# 給類添加sealed關鍵字防止子類繼承產生多個單例、給靜態欄位添加readonly修改為只讀狀態,意味著只能在靜態初始化期間或在類構造函數中分配變數。

這種靜態初始化的方式是在自己被載入時就將自己實例化,所以被形象地稱之為餓漢式單例類,原先的單例模式處理方式是要在被第一次引用時才會自己實例化,這叫懶漢式初始化。

餓漢式初始化是類一載入就實例化的對象,所以要提前占用系統資源。然而懶漢式,又會面臨多線程訪問的安全性問題,需要做雙重鎖定才能保證安全。

在golang實現靜態初始化,實際上只要把實例化的過程移動到當前文件的init函數中,在包被載入的過程中,會首先運行各個文件的init函數,再運行main函數。

實用類與單例類的比較

在C#經常有工具類之說,這個工具類包含許多靜態方法和靜態屬性。但是這種實用類沒有單例類的狀態。實用類不能用於繼承多台,而單例類雖然實例唯一,但是可以有子類來繼承。實用類是一些方法屬性的集合,單例是有著唯一對象的實例。

參考資料

  • 《Go語言核心編程》李文塔
  • 《Go語言高級編程》柴樹彬、曹春輝
  • 《大話設計模式》程傑
  • 單例模式 | 菜鳥教程

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

-Advertisement-
Play Games
更多相關文章
  • 目錄 一.OpenGL 飽和度調節效果演示 1.IOS 飽和度演示效果 2.Windows OpenGL ES 飽和度演示效果 3.Windows OpenGL 飽和度演示效果 二.OpenGL 飽和度調節源碼下載 1.IOS Object-C 版本 2.Windows OpenGL ES 版本 3 ...
  • 一個菜鳥的設計模式之旅,使用 Golang 實現。本節實現解釋器模式。程式可按需載入用戶自定義的.work尾碼文件,將每行的命令解釋為具體行為。喵叫幾次、進程休眠幾秒、輸出範圍內隨機數、運行另外的work文件。 ...
  • #一、format的基本玩法 ##🚀🚀一、什麼是format format是字元串內嵌(字元串內嵌:字元串中再嵌套字元串,加入雙引號或單引號)的一個方法,用於格式化字元串。以大括弧{}來標明被替換的字元串 ##🚀🚀format玩法一:按順序輸出(按照{}的順序依次匹配括弧中的值) >>>s ...
  • IO流01 1.文件基礎知識 什麼是文件? 文件,我們並不陌生。文件是保存數據的地方。比如大家經常使用的word文檔,txt文件,excel文件等,都是文件。它既可以保存一張圖片,也可以保存聲音、視頻…… 文件流 文件在程式中是以流的形式來操作的: 流:數據在數據源(文件)和程式(記憶體)之間經歷的路 ...
  • 說明 意義 1.在Spring中,Bean的作用域可以通過scope屬性來指定。 2.指定作用域的目的是 存儲在此類單例bean的高速緩存中,並且對該命名bean的所有後續請求和引用都返回該高速緩存的對象。(本身的理念就是以空間換時間的思維,創建步驟繁雜,而且頻繁用到,我就存起來,下次用的時候就不用 ...
  • 摘要:本篇文章主要講解基於理論的圖像分割方法,通過K-Means聚類演算法實現圖像分割或顏色分層處理。 本文分享自華為雲社區《[Python圖像處理] 十九.圖像分割之基於K-Means聚類的區域分割》,作者: eastmount。 本篇文章主要講解基於理論的圖像分割方法,通過K-Means聚類演算法實 ...
  • pring Boot Actuator 是 Spring Boot 提供的對應用的自省和監控功能,如健康檢查,審計,指標收集,HTTP 跟蹤等,可以幫助開發和運維人員監控和管理 Spring Boot 應用。該模塊採集應用的內部信息,並暴露給外部的模塊,支持 HTTP 和 JMX,並可以與一些第三方... ...
  • 鋼鐵知識庫,一個學習python爬蟲、數據分析的知識庫。人生苦短,快用python。 之前我們使用requests庫爬取某個站點的時候,每發出一個請求,程式必須等待網站返迴響應才能接著運行,而在整個爬蟲過程中,整個爬蟲程式是一直在等待的,實際上沒有做任何事情。 像這種占用磁碟/記憶體IO、網路IO的任 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...