這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。多線程應用程式非常複雜,尤其是當你的代碼沒有組織並且與資源訪問、管理和維護保持一致時。如果你想最大限度地減少錯誤,你需要哲學和規則來生活。這裡有一些我的:- 資源的分配和回收應該在同一類型中抽象和管理- 資源執行緒安全性應該在同一類型中抽象和管理- 公用介面應該是訪問共用資源的唯一手段- 任何分配了資源的線程都應該釋放同類型的資源在 Go 中,沒有線程,只有 `Go Routines`。Go 運行時抽象了這些常式的線程和任務交換。無論如何,相同的哲學和規則也適用。我最喜歡的設計模式之一是單例模式(Singleton)。當你只需要一個類型的執行個體並且該類型管理共用資源時,它提供了一個很好的實現。訪問由該引用管理的共用資源是通過靜態公用介面抽象出來的。這些靜態方法還提供了執行緒安全性。使用 Singleton 的應用程式負責初始化和銷毀 Singleton,但不能直接存取內部。在一段時間內,我迴避如何在 Go 中實現一個單例的問題,因為 Go 不是傳統的物件導向的程式設計語言,也沒有靜態方法。我認為 Go 是一個輕量級的物件導向程式設計語言。是的,它確實具有封裝和類型成員函數,但缺乏繼承性,因此缺乏傳統的多態性。在我曾經使用過的所有 OOP 語言中,除非我想實現多態性,否則我從來沒有使用過繼承。 在 Go 中實現介面的方式不需要繼承。 Go 取了 OOP 最好的部分,移除了剩下的部分,並給了我們一個更好的方式來編寫多態代碼。在 Go 中我們可以利用包和類型的範圍和封裝規則來實現 Singleton,對於這篇文章,我們將探索我的 straps 包,因為它將給我們一個現實世界的例子。straps 包提供了一種機制來將配置選項(straps)儲存在XML文檔中,並將其讀入記憶體以供應用程式使用。straps 的名稱來自配置網路裝置的早期階段。 這些設定被稱為 straps,並且這個名字一直伴隨著我。 在 MacOS 中,我們有 .plist 檔案,在 .Net 中我們有 app.config 檔案,在 Go 中有 straps.xml 檔案。以下是我的一個應用程式的樣本 straps 檔案:```xml<straps><!– Log Settings –><strap key="baseFilePath" value="/Users/bill/Logs/OC-DataServer"><strap key="machineName" value="my-machine"><strap key="daysToKeep" value="1"><!– ServerManager Settings –><strap key="cpuMultiplier" value="100"></straps>```straps 包知道如何讀取這個 xml 檔案,並通過基於 Singleton 的公用介面提供對這些值的訪問。 由於這些值只需要讀入記憶體中,因此 Singleton 對於這個包來說是一個很好的選擇。以下是 straps 包和類型資訊:```gopackage strapsimport ("encoding/xml""io""os""path/filepath""strconv").. Types Removed.type straps struct {StrapMap map[string]string // The map of strap key value pairs}var st straps // A reference to the singleton```我不會談論讀取 XML 文檔的內容。 如果你有興趣,請閱讀這篇博文 http://www.goinggo.net/2013/06/reading-xml-documents-in-go.html。在上面的程式碼片段中,您將看到包名稱(straps),私人類型 straps 的定義和私人包變數 st。st 變數將包含 Singleton 的值。Go 的範圍規則聲明以大寫字母開頭的類型和函數是公開的,並且可以在包外部存取。以小寫字母開頭的類型和函數是私人的,在包之外不可訪問。我使用小寫字母命名在函數範圍內定義的變數。在函數範圍之外定義的變數名稱,例如類型成員和包變數以大寫字母開頭。這使我可以查看代碼並立即知道引用了哪個給定變數的記憶體。straps 類型和 st 變數都是私人的,只能從包中訪問。查看初始化 Singleton 以供使用的 Load 函數:```gofunc MustLoad() {// Find the location of the straps.xml filestrapsFilePath, err := filepath.Abs("straps.xml")// Open the straps.xml filefile, err := os.Open(strapsFilePath)if err != nil {panic(err.Error())}defer file.Close()// Read the straps filexmlStraps, err := readStraps(file)if err != nil {panic(err.Error())}// Create a straps objectst = straps{StrapMap: make(map[string]string),}// Store the key/value pairs for each strapfor _, strap := range xmlStraps {st.StrapMap[strap.Key] = strap.Value}}```Load 函數是包的公有函數。應用程式可以通過包名來訪問這個函數。你可以看到我如何使用以局部變數的小寫字母開頭的名稱。在 Load 函數的底部建立一個 straps 對象,並將該引用設定為 st 變數。在這一點上,Singleton 存在並且 straps 已經可以使用了。使用公有函數 Strap 訪問 straps:```gofunc Strap(key string) string {return st.StrapMap[key]}```公有函數 Strap 使用 Singleton 引用來訪問共用資源。在這個例子中,就是 straps 的字典映射。 如果字典映射在應用程式的生命週期內可能發生變化,則需要使用互斥鎖或其他同步對象來保護字典映射。 幸運的是,straps 一旦被裝載就不會改變。由於由 straps 管理的資源只是記憶體,因此不需要 Unload 或 Close 方法。如果我們需要一個函數來關閉任何資源,則必須建立另一個公有函數。如果在 Singleton 包中需要私人方法來協助組織代碼,我喜歡使用成員函數。 由於類型是私人的,我可以使成員函數公開,因為它們不可訪問。 我也認為成員函數有助於使代碼更具可讀性。 通過查看函數是否是成員函數,我知道該函數是私人的還是公用介面的一部分。```gofunc SomePublicFunction() {.st.SomePrivateMemberFunction("key").}func (straps *straps) SomePrivateMemberFunction(key string) {return straps.StrapMap[key].}```由於函數是一個成員函數,我們需要使用 st 變數來進行函數調用。從成員函數內使用局部變數(straps)而不是 st 變數。成員函數是公用的,但引用是私人的,所以只有包可以引用成員函數。這隻是我為自己建立的一個習慣。下面是一個使用 straps 包的樣本程式:```gopackage mainimport ("ArdanStudios/straps")func main() {straps.MustLoad()cpu := straps.Strap("cpuMultiplier")}```在 main 中我們不需要分配任何記憶體或保持引用。通過包名,我們調用 Load 來初始化 Singleton。然後再次通過包名稱訪問公用介面,在這種情況下,是 Strap 函數。如果你有通過公用介面嘗試來管理共用資源的需求,就使用 Singleton。和往常一樣,我希望這可以協助您更好地編寫代碼並減少 bug。
via: https://www.ardanlabs.com/blog/2013/07/singleton-design-pattern-in-go.html
作者:William Kennedy 譯者:shniu 校對:polaris1119
本文由 GCTT 原創編譯,Go語言中文網 榮譽推出
本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻嗎?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在本文中標註並保留原文/譯文連結和作者/譯者等資訊。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽
554 次點擊