這篇文章想聊聊Golang語言下的設計模式問題,我覺得這個話題還是比較有意思的。Golang沒有像java那樣對設計模式瘋狂的迷戀,而是擺出了一份“看庭前花開花落,望天空云卷云舒”的姿態(tài)。
單例模式:
Golang的單例模式該怎么寫?隨手寫一個,不錯,立馬寫出來了。但這個代碼有什么問題呢?多個協(xié)程同時執(zhí)行這段代碼就會出現(xiàn)問題:instance可能會被賦值多次,這段代碼是線程不安全的代碼。那么如何保證在多線程下只執(zhí)行一次呢?條件反射:加鎖。。。加鎖是可以解決問題。但不是最優(yōu)的方案,因為如果有1W并發(fā),每一個線程都競爭鎖,同一時刻只有一個線程能拿到鎖,其他的全部阻塞等待。讓原本想并發(fā)得飛起來變成了一切認慫串行化。通過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
}
工廠模式:
工廠根據(jù)條件產生不同功能的類。工廠模式使用經(jīng)常使用在替代new的場景中,讓工廠統(tǒng)一根據(jù)不同條件生產不同的類。工廠模式在解耦方面將使用者和產品之間的依賴推給了工廠,讓工廠承擔這種依賴關系。工廠模式又分為簡單工廠,抽象工廠。golang實現(xiàn)一個簡單工廠模式如下:
package main
import (
"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())
}
依賴注入:
具體含義是:當某個角色(可能是一個實例,調用者)需要另一個角色(另一個實例,被調用者)的協(xié)助時,在傳統(tǒng)的程序設計過程中,通常由調用者來創(chuàng)建被調用者的實例。但在這種場景下,創(chuàng)建被調用者實例的工作通常由容器(IoC)來完成,然后注入調用者,因此也稱為依賴注入。
Golang利用函數(shù)f可以當做參數(shù)來傳遞,同時配合reflect包拿到參數(shù)的類型,然后根據(jù)調用者傳來的參數(shù)和類型匹配上之后,最后通過reflect.Call()執(zhí)行具體的函數(shù)。下面的代碼來自:https://www.studygolang.com/articles/4957 這篇文章上。
package main
import (
"fmt"
"reflect"
)
var inj *Injector
type Injector struct {
mappers map[reflect.Type]reflect.Value // 根據(jù)類型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() {
// 創(chuàng)建注入器
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)
}
裝飾器模式:
裝飾器模式:允許向一個現(xiàn)有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬于結構型模式,它是作為現(xiàn)有的類的一個包裝。這種模式創(chuàng)建了一個裝飾類,用來包裝原有的類,并在保持類方法簽名完整性的前提下,提供了額外的功能。我們使用最為頻繁的場景就是http請求的處理:對http請求做cookie校驗。
package main
import (
"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中是如何體現(xiàn)出來的,框架或者類庫應該是設計模式常常出沒的地方。深入理解設計模式有助于代碼的抽象,復用和解耦,讓代碼與代碼之間更加低耦合。