這篇文章想聊聊Golang
語言下的設(shè)計模式問題,我覺得這個話題還是比較有意思的箫踩。Golang沒有像java那樣對設(shè)計模式瘋狂的迷戀爱态,而是擺出了一份“看庭前花開花落境钟,望天空云卷云舒”的姿態(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ù)條件產(chǎn)生不同功能的類代芜。工廠模式使用經(jīng)常使用在替代new的場景中呼寸,讓工廠統(tǒng)一根據(jù)不同條件生產(chǎn)不同的類捆憎。工廠模式在解耦方面將使用者和產(chǎn)品之間的依賴推給了工廠郊闯,讓工廠承擔(dān)這種依賴關(guān)系闺属。工廠模式又分為簡單工廠计呈,抽象工廠砰诵。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())
}
依賴注入:
具體含義是:當(dāng)某個角色(可能是一個實例捌显,調(diào)用者)需要另一個角色(另一個實例茁彭,被調(diào)用者)的協(xié)助時扶歪,在傳統(tǒng)的程序設(shè)計過程中理肺,通常由調(diào)用者來創(chuàng)建被調(diào)用者的實例。但在這種場景下妹萨,創(chuàng)建被調(diào)用者實例的工作通常由容器(IoC)來完成,然后注入調(diào)用者炫欺,因此也稱為依賴注入。
Golang利用函數(shù)f可以當(dāng)做參數(shù)來傳遞品洛,同時配合reflect包拿到參數(shù)的類型树姨,然后根據(jù)調(diào)用者傳來的參數(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)有的對象添加新的功能台丛,同時又不改變其結(jié)構(gòu)。這種類型的設(shè)計模式屬于結(jié)構(gòu)型模式,它是作為現(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)出來的,框架或者類庫應(yīng)該是設(shè)計模式常常出沒的地方淆党。深入理解設(shè)計模式有助于代碼的抽象,復(fù)用和解耦讶凉,讓代碼與代碼之間更加低耦合。