日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

本文轉載自微信公眾號「洋芋編程」,作者蠻荊 。轉載本文請聯系洋芋編程公眾號。

在軟件工程中,設計模式(design pattern)是對軟件設計中普遍存在(反復出現)的各種問題,所提出的解決方案。 -- 維基百科

和傳統的 GOF, JAVA, C# 教科書式的 設計模式 不同,Go 語言設計從一開始就力求簡潔,有其他編程語言基礎的讀者在學習和使用 Go 語言時, 萬萬不可按圖索驥、生搬硬套,簡單的事情復雜化。

本文帶領大家一起看一下,Go 語言標準庫中自帶的 編程設計模式。

單例模式

確保一個類只有一個實例,并提供對該實例的全局訪問。

通過使用標準庫中的 sync.Once 對業務對象進行簡單封裝,即可實現 單例模式,簡單安全高效。

package main

import "sync"

var (
    once     sync.Once
    instance Singleton
)

// Singleton 業務對象
type Singleton struct {
}

// NewInstance 單例模式方法
func NewInstance() Singleton {
    once.Do(func() {
        instance = Singleton{}
    })
    return instance
}

func main() {
    // 調用方代碼
    s1 := NewInstance()
    s2 := NewInstance()
    s3 := NewInstance() 
}

 

圖片

 

Go 標準庫單例模式

簡單工廠模式

Go 語言本身沒有 構造方法 特性,工程實踐中一般使用 NewXXX 創建新的對象 (XXX 為對象名稱),比如標準庫中的:

// errors/errors.go

func New(text string) error {
    return &errorString{text}
}

// sync/cond.go
func NewCond(l Locker) *Cond {
    return &Cond{L: l}
}

在這個基礎上,如果方法返回的是 interface 的時候,其實就等于是 簡單工廠模式,然后再加一層抽象的話,就接近于 抽象工廠模式。

package main

// ConfigParser 配置解析接口
type ConfigParser interface {
    Parse(p []byte)
}

// JsonParser Json 文件解析器
type JsonParser struct {
}

func (j *JsonParser) Parse(p []byte) {

}

func newJsonParser() *JsonParser {
    return &JsonParser{}
}

// YamlParser Yaml 文件解析器
type YamlParser struct {
}

func (y *YamlParser) Parse(p []byte) {

}

func newYamlParser() *YamlParser {
    return &YamlParser{}
}

type ConfigType uint8

const (
    JsonType ConfigType = 1 << iota
    YamlType
)

// NewConfig 根據不同的類型創建對應的解析器
func NewConfig(t ConfigType) ConfigParser {
    switch t {
    case JsonType:
        return newJsonParser()
    case YamlType:
        return newYamlParser()
    default:
        return nil
    }
}

func main() {
    // 調用方代碼
    jsonParser := NewConfig(JsonType)
    yamlParser := NewConfig(YamlType)
}

 

圖片

 

Go 實現簡單工廠模式

對象池模式

通過回收利用對象避免獲取和釋放資源所需的昂貴成本,我們可以直接使用 sync.Pool 對象來實現功能。

package main

import (
    ".NET/http"
    "sync"
)

var (
    // HTTP Request 對象池
    reqPool = sync.Pool{
        New: func() any {
            return http.Request{}
        },
    }
)

func main() {
    // 調用方代碼
    r1 := reqPool.Get()
    r2 := reqPool.Get()
    r3 := reqPool.Get()

    reqPool.Put(r1)
    reqPool.Put(r2)
    reqPool.Put(r3)
}
 

構建模式 (Builder)

將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。

如果用傳統的方法實現 構建模式,對應的 Go 語言代碼大致是下面這個樣子:

package main

type QueryBuilder interface {
    Select(table string, columns []string) QueryBuilder
    Where(conditions ...string) QueryBuilder
    GetRawSQL() string
}

type MySQLQueryBuilder struct {
}

func (m *MySQLQueryBuilder) Select(table string, columns ...string) QueryBuilder {
    // 具體實現代碼跳過
    return nil
}

func (m *MySQLQueryBuilder) Where(conditions ...string) QueryBuilder {
    // 具體實現代碼跳過
    return nil
}

func (m *MySQLQueryBuilder) GetRawSQL() string {
    // 具體實現代碼跳過
    return ""
}

func main() {
    // 調用方代碼
    m := &MySQLQueryBuilder{}

    sql := m.Select("users", "username", "password").
        Where("id = 100").
        GetRawSQL()

    println(sql)
}

 

圖片

 

Go 實現構建模式

上面的代碼中,通過經典的鏈式調用來構造出具體的 SQL 語句,但是在 Go 語言中,我們一般使用另外一種模式來實現同樣的功能 FUNCTIONAL OPTIONS, 這似乎也是 Go 語言中最流行的模式之一。

package main

type SQL struct {
    Table   string
    Columns []string
    Where   []string
}

type Option func(s *SQL)

func Table(t string) Option {
    // 注意返回值類型
    return func(s *SQL) {
        s.Table = t
    }
}

func Columns(cs ...string) Option {
    // 注意返回值類型
    return func(s *SQL) {
        s.Columns = cs
    }
}

func Where(conditions ...string) Option {
    // 注意返回值類型
    return func(s *SQL) {
        s.Where = conditions
    }
}

func NewSQL(options ...Option) *SQL {
    sql := &SQL{}

    for _, option := range options {
        option(sql)
    }

    return sql
}

func main() {
    // 調用方代碼
    sql := NewSQL(Table("users"),
        Columns("username", "password"),
        Where("id = 100"),
    )

    println(sql)
}

 

圖片

 

Go FUNCTIONAL OPTIONS 模式

觀察者模式

在對象間定義一個一對多的聯系性,由此當一個對象改變了狀態,所有其他相關的對象會被通知并且自動刷新。

如果用傳統的方法實現 觀察者模式,對應的 Go 語言代碼大致是下面這個樣子:

package main

import "math"

// Observer 觀察者接口
type Observer interface {
    OnNotify(Event)
}

// Notifier 訂閱接口
type Notifier interface {
    Register(Observer)
    Deregister(Observer)
    Notify(Event)
}

type (
    Event struct {
        Data int64
    }

    eventObserver struct {
        id int
    }

    eventNotifier struct {
        observers map[Observer]struct{}
    }
)

// OnNotify 觀察者收到訂閱的時間回調
func (o *eventObserver) OnNotify(e Event) {
}

// Register 注冊觀察者
func (o *eventNotifier) Register(l Observer) {
    o.observers[l] = struct{}{}
}

// Deregister 移除觀察者
func (o *eventNotifier) Deregister(l Observer) {
    delete(o.observers, l)
}

// Notify 發出通知
func (o *eventNotifier) Notify(e Event) {
    for p := range o.observers {
        p.OnNotify(e)
    }
}

func main() {
    // 調用方代碼
    notifier := eventNotifier{
        observers: make(map[Observer]struct{}),
    }

    notifier.Register(&eventObserver{1})
    notifier.Register(&eventObserver{2})
    notifier.Register(&eventObserver{3})

    notifier.Notify(Event{Data: math.MaxInt64})
}
 

圖片

 

Go 實現觀察者模式

但其實我們有更簡潔的方法,直接使用標準庫中的 sync.Cond 對象,改造之后的 觀察者模式 代碼大概是這個樣子:

 

package main

import (
    "fmt"
    "sync"
    "time"
)

var done = false

func read(name string, c *sync.Cond) {
    fmt.Println(name, "starts reading")

    c.L.Lock()
    for !done {
        c.Wait() // 等待發出通知
    }
    c.L.Unlock()
}

func write(name string, c *sync.Cond) {
    fmt.Println(name, "starts writing")
    time.Sleep(100 * time.Millisecond)

    c.L.Lock()
    done = true // 設置條件變量
    c.L.Unlock()

    fmt.Println(name, "wakes all")
    c.Broadcast() // 通知所有觀察者
}

func main() {
    cond := sync.NewCond(&sync.Mutex{}) // 創建時傳入一個互斥鎖

    // 3 個觀察者
    go read("reader1", cond)
    go read("reader2", cond)
    go read("reader3", cond)

    time.Sleep(time.Second) // 模擬延時

    write("writer-1", cond) // 發出通知

    time.Sleep(time.Second) // 模擬延時
}

 

圖片

 

Go 標準庫觀察者模式

將代碼改造為 sync.Cond 之后,代碼量更好,結構更簡潔。

ok/error 模式

在 Go 語言中,經常在一個表達式返回 2 個參數時使用這種模式:

  • 第 1 個參數是一個值或者 nil
  • 第 2 個參數是 true/false 或者 error

在一個需要賦值的 if 條件語句中,使用這種模式去檢測第 2 個參數值會讓代碼顯得優雅簡潔。

在函數返回時檢測錯誤

 

package main

func foo() (int, error){
    return 0, nil
}

func main() {
    if v, err := foo(); err != nil {
        panic(err)
    } else {
        println(v)
    }
}

 

檢測 map 是否存在指定的 key

 

package main

func main() {
    m := make(map[int]string)

    if v, ok := m[0]; ok {
        println(v)
    }
}

 

類型斷言

 

package main

func foo() interface{} {
    return 1024
}

func main() {
    n := foo()
    if v, ok := n.(int); ok {
        println(v)
    }
}

 

檢測通道是否關閉

 

package main

func main() {
    ch := make(chan int)

    go func() {
        for i := 0; i < 5; i++ {
            ch <- i
        }
        close(ch)
    }()

    for {
        if v, ok := <-ch; ok {
            println(v)
        } else {
            return
        }
    }
}

// $ go run main.go
// 輸出如下
// 0
// 1
// 2
// 3
// 4

 

附加內容

閉包

有時候,我們可以利用 閉包 實現一些短小精悍的內部函數。

計數器

package main

func main() {
    newSeqInc := func() func() int {
        seq := 0
        return func() int {
            seq++
            return seq
        }
    }

    seq := newSeqInc() // 創建一個計數器
    println(seq())     // 1
    println(seq())     // 2
    println(seq())     // 3

    seq2 := newSeqInc() // 創建另一個計數器
    println(seq2())     // 1
    println(seq2())     // 2
    println(seq2())     // 3
}

 

小結

下面表格列出了常用的 設計模式,其中 Go 標準庫自帶的 模式 已經用刪除線標識,讀者可以和自己常用的 設計模式 進行對比。

創建型模式

結構性模式

行為型模式

單例

適配器

策略

簡單工廠

裝飾者

觀察者

抽象工廠

代理

狀態

對象池

 

責任鏈

構建

 

 

長期以來,設計模式 一直處于尷尬的位置:初學者被各種概念和關系搞得不知所云,有經驗的程序員會覺得 “這種代碼寫法 (這里指設計模式),我早就知道了啊”。 鑒于這種情況,本文中沒有涉及到的 設計模式,筆者不打算再一一描述,感興趣的讀者可以直接跳到 倉庫代碼[1] 查看示例代碼。

相比于設計模式,更重要的是理解語言本身的特性以及最佳實踐。

擴展閱讀

  •  Go 與面向對象
  • 設計模式 - 維基百科[2]
  • go-examples-for-beginners/patterns[3]
  • 圣杯與銀彈 · 沒用的設計模式[4]
  • tmrts/go-patterns[5]
  • DESIGN PATTERNS in GO[6]
  • 解密“設計模式”[7]
  • Go 編程模式 - 酷殼[8]

引用鏈接

[1] 倉庫代碼: https://github.com/duanbiaowu/go-examples-for-beginners/tree/master/patterns

[2] 設計模式 - 維基百科: https://zh.wikipedia.org/wiki/設計模式_(計算機)

[3]? go-examples-for-beginners/patterns: ??https://github.com/duanbiaowu/go-examples-for-beginners/tree/master/patterns??

[4] 圣杯與銀彈 · 沒用的設計模式: https://draveness.me/holy-grail-design-pattern/

[5]? tmrts/go-patterns: ??https://github.com/tmrts/go-patterns??

[6] DESIGN PATTERNS in GO: https://refactoring.guru/design-patterns/go

[7] 解密“設計模式”: ??http://www.yinwang.org/blog-cn/2013/03/07/design-patterns??

[8] Go 編程模式 - 酷殼: https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f

分享到:
標簽:語言
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定