Golang視角下的設(shè)計模式

這篇文章想聊聊Golang語言下的設(shè)計模式問題,我覺得這個話題還是比較有意思的箫踩。Golang沒有像java那樣對設(shè)計模式瘋狂的迷戀爱态,而是擺出了一份“看庭前花開花落境钟,望天空云卷云舒”的姿態(tài)锦担。

單例模式:

Golang的單例模式該怎么寫吱韭?隨手寫一個,不錯鱼的,立馬寫出來了。但這個代碼有什么問題呢凑阶?多個協(xié)程同時執(zhí)行這段代碼就會出現(xiàn)問題:instance可能會被賦值多次猿规,這段代碼是線程不安全的代碼宙橱。那么如何保證在多線程下只執(zhí)行一次呢姨俩?條件反射:加鎖。环葵。。加鎖是可以解決問題宝冕。但不是最優(yōu)的方案,因為如果有1W并發(fā)地梨,每一個線程都競爭鎖菊卷,同一時刻只有一個線程能拿到鎖,其他的全部阻塞等待洁闰。讓原本想并發(fā)得飛起來變成了一切認慫串行化歉甚。通過check-lock-check方式可以減少競爭扑眉。還有其他方式纸泄,利用sync/atomicsync/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ù)用和解耦讶凉,讓代碼與代碼之間更加低耦合。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末懂讯,一起剝皮案震驚了整個濱河市荷憋,隨后出現(xiàn)的幾起案子褐望,更是在濱河造成了極大的恐慌勒庄,老刑警劉巖瘫里,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件实蔽,死亡現(xiàn)場離奇詭異减宣,居然都是意外死亡盐须,警方通過查閱死者的電腦和手機玩荠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來阶冈,“玉大人闷尿,你說我怎么就攤上這事女坑√罹撸” “怎么了?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵劳景,是天一觀的道長誉简。 經(jīng)常有香客問我,道長闷串,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任筋量,我火速辦了婚禮,結(jié)果婚禮上桨武,老公的妹妹穿的比我還像新娘肋拔。我一直安慰自己呀酸,他們只是感情好凉蜂,可當(dāng)我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布七咧。 她就那樣靜靜地躺著跃惫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪艾栋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天蝗砾,我揣著相機與錄音,去河邊找鬼悼粮。 笑死闲勺,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的菜循。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼申尤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了昧穿?” 一聲冷哼從身側(cè)響起勺远,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤时鸵,失蹤者是張志新(化名)和其女友劉穎胶逢,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體初坠,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡和簸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年碟刺,在試婚紗的時候發(fā)現(xiàn)自己被綠了比搭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡身诺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出抄囚,到底是詐尸還是另有隱情霉赡,我是刑警寧澤幔托,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布穴亏,位于F島的核電站重挑,受9級特大地震影響嗓化,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜谬哀,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望史煎。 院中可真熱鬧谦屑,春花似錦篇梭、人聲如沸氢橙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至袍患,卻和暖如春坦康,著一層夾襖步出監(jiān)牢的瞬間协怒,已是汗流浹背涝焙。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工卑笨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓妖滔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親座舍。 傳聞我的和親對象是個殘疾皇子沮翔,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,860評論 2 361

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,325評論 25 707
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法曲秉,類相關(guān)的語法采蚀,內(nèi)部類的語法,繼承相關(guān)的語法榆鼠,異常的語法,線程的語...
    子非魚_t_閱讀 31,665評論 18 399
  • 在生活或者工作中负蚊,我們經(jīng)常會為了給別人留下一個好的印象神妹,去做一些事情。比如面試家妆,在面試之前我們得做好充足的準(zhǔn)備,給...
    小小青橙閱讀 319評論 0 2
  • 標(biāo)題很奇怪吧伤极。怎么會有人不喜歡喜歡的人呢腰鬼? 偶然間在微博里看到一種病塑荒,別人不喜歡你的時候你千方百計地靠近他熄赡,想得到...
    再美劇本也有劇終時閱讀 251評論 0 0
  • 你靜靜的 讓夜色吞噬 找不到 對你眨眼的星星 也找不到 那片微笑的云 孤寂 是素色的靈魂 曾牽引著 疲憊的腳步 漫...
    饅頭兒閱讀 328評論 1 1