這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
這篇文章想聊聊Golang語言下的設計模式問題,我覺得這個話題還是比較有意思的。Golang沒有像java那樣對設計模式瘋狂的迷戀,而是擺出了一份“看庭前花開花落,望天空雲捲雲舒”的姿態。
單例模式:
Gloang的單例模式該怎麼寫?隨手寫一個,不錯,立馬寫出來了。但這個代碼有什麼問題呢?多個協程同時執行這段代碼就會出現問題:instance可能會被賦值多次,這段代碼是線程不安全的代碼。那麼如何保證在多線程下只執行一次呢?條件反射:加鎖。。。加鎖是可以解決問題。但不是最優的方案,因為如果有1W並發,每一個線程都競爭鎖,同一時刻只有一個線程能拿到鎖,其他的全部阻塞等待。讓原本想並發得飛起來變成了一切認慫序列化。通過check-lock-check方式可以減少競爭。還有其他方式,利用sync/atomic和sync/once 這裡只給出代碼
func NewSingleton() *singleton { if instance == nil { instance = &singleton{} } return instance}
func NewSingleton() *singleton { l.Lock() // lock defer l.Unlock() if instance == nil { // check instance = &singleton{} } return instance}
func NewSingleton() *singleton { if instance == nil { // check l.Lock() // lock defer l.Unlock() if instance == nil { // check instance = &singleton{} } } return instance}
func NewSingleton() *singleton { if atomic.LoadUInt32(&initialized) == 1 { return instance } mu.Lock() defer mu.Unlock() if initialized == 0 { instance = &singleton{} atomic.StoreUint32(&initialized, 1) } return instance}
func NewSingleton() *singleton { once.Do(func() { instance = &singleton{} }) return instance}
原廠模式:
工廠根據條件產生不同功能的類。原廠模式使用經常使用在替代new的情境中,讓工廠統一根據不同條件生產不同的類。原廠模式在解耦方面將使用者和產品之間的依賴推給了工廠,讓工廠承擔這種依賴關係。原廠模式又分為簡單工廠,抽象工廠。golang實現一個簡單原廠模式如下:
package mainimport ( "fmt")type Op interface { getName() string}type A struct {}type B struct {}type Factory struct {}func (a *A) getName() string { return "A"}func (b *B) getName() string { return "B"}func (f *Factory) create(name string) Op { switch name { case `a`: return new(A) case `b`: return new(B) default: panic(`name not exists`) } return nil}func main() { var f = new(Factory) p := f.create(`a`) fmt.Println(p.getName()) p = f.create(`b`) fmt.Println(p.getName())}
依賴注入:
具體含義是:當某個角色(可能是一個執行個體,調用者)需要另一個角色(另一個執行個體,被調用者)的協助時,在傳統的程式設計過程中,通常由調用者來建立被調用者的執行個體。但在這種情境下,建立被調用者執行個體的工作通常由容器(IoC)來完成,然後注入調用者,因此也稱為依賴注入。
Golang利用函數f可以當做參數來傳遞,同時配合reflect包拿到參數的類型,然後根據調用者傳來的參數和類型匹配上之後,最後通過reflect.Call()執行具體的函數。下面的代碼來自:https://www.studygolang.com/articles/4957 這篇文章上。
package mainimport ( "fmt" "reflect")var inj *Injectortype Injector struct { mappers map[reflect.Type]reflect.Value // 根據類型map實際的值}func (inj *Injector) SetMap(value interface{}) { inj.mappers[reflect.TypeOf(value)] = reflect.ValueOf(value)}func (inj *Injector) Get(t reflect.Type) reflect.Value { return inj.mappers[t]}func (inj *Injector) Invoke(i interface{}) interface{} { t := reflect.TypeOf(i) if t.Kind() != reflect.Func { panic("Should invoke a function!") } inValues := make([]reflect.Value, t.NumIn()) for k := 0; k < t.NumIn(); k++ { inValues[k] = inj.Get(t.In(k)) } ret := reflect.ValueOf(i).Call(inValues) return ret}func Host(name string, f func(a int, b string) string) { fmt.Println("Enter Host:", name) fmt.Println(inj.Invoke(f)) fmt.Println("Exit Host:", name)}func Dependency(a int, b string) string { fmt.Println("Dependency: ", a, b) return `injection function exec finished ...`}func main() { // 建立注入器 inj = &Injector{make(map[reflect.Type]reflect.Value)} inj.SetMap(3030) inj.SetMap("zdd") d := Dependency Host("zddhub", d) inj.SetMap(8080) inj.SetMap("www.zddhub.com") Host("website", d)}
裝飾器模式:
裝飾器模式:允許向一個現有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬於結構型模式,它是作為現有的類的一個封裝。這種模式建立了一個裝飾類,用來封裝原有的類,並在保持類方法簽名完整性的前提下,提供了額外的功能。我們使用最為頻繁的情境就是http請求的處理:對http請求做cookie校正。
package mainimport ( "fmt" "log" "net/http")func autoAuth(h http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("Auth") if err != nil || cookie.Value != "Authentic" { w.WriteHeader(http.StatusForbidden) return } h(w, r) }}func hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, World! "+r.URL.Path)}func main() { http.HandleFunc("/hello", autoAuth(hello)) err := http.ListenAndServe(":5666", nil) if err != nil { log.Fatal("ListenAndServe: ", err) }}
還有很多其他模式,這裡不一一給出了,寫這篇文章的目的是想看看這些模式在golang中是如何體現出來的,架構或者類庫應該是設計模式常常出沒的地方。深入理解設計模式有助於代碼的抽象,複用和解耦,讓代碼與代碼之間更加低耦合。