自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

聊聊 Go 語言自帶設(shè)計(jì)模式

開發(fā) 前端
和傳統(tǒng)的 GOF?, Java?, C#? 教科書式的 設(shè)計(jì)模式 不同,Go 語言設(shè)計(jì)從一開始就力求簡(jiǎn)潔,有其他編程語言基礎(chǔ)的讀者在學(xué)習(xí)和使用 Go 語言時(shí), 萬萬不可按圖索驥、生搬硬套,簡(jiǎn)單的事情復(fù)雜化。本文帶領(lǐng)大家一起看一下,Go 語言標(biāo)準(zhǔn)庫(kù)中自帶的 編程設(shè)計(jì)模式。

本文轉(zhuǎn)載自微信公眾號(hào)「洋芋編程」,作者蠻荊 。轉(zhuǎn)載本文請(qǐng)聯(lián)系洋芋編程公眾號(hào)。

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

和傳統(tǒng)的 GOF, Java, C# 教科書式的 設(shè)計(jì)模式 不同,Go 語言設(shè)計(jì)從一開始就力求簡(jiǎn)潔,有其他編程語言基礎(chǔ)的讀者在學(xué)習(xí)和使用 Go 語言時(shí), 萬萬不可按圖索驥、生搬硬套,簡(jiǎn)單的事情復(fù)雜化。

本文帶領(lǐng)大家一起看一下,Go 語言標(biāo)準(zhǔn)庫(kù)中自帶的 編程設(shè)計(jì)模式。

單例模式

確保一個(gè)類只有一個(gè)實(shí)例,并提供對(duì)該實(shí)例的全局訪問。

通過使用標(biāo)準(zhǔn)庫(kù)中的 sync.Once 對(duì)業(yè)務(wù)對(duì)象進(jìn)行簡(jiǎn)單封裝,即可實(shí)現(xiàn) 單例模式,簡(jiǎn)單安全高效。

package main

import "sync"

var (
once sync.Once
instance Singleton
)

// Singleton 業(yè)務(wù)對(duì)象
type Singleton struct {
}

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

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

圖片

Go 標(biāo)準(zhǔn)庫(kù)單例模式

簡(jiǎn)單工廠模式

Go 語言本身沒有 構(gòu)造方法 特性,工程實(shí)踐中一般使用 NewXXX 創(chuàng)建新的對(duì)象 (XXX 為對(duì)象名稱),比如標(biāo)準(zhǔn)庫(kù)中的:

// errors/errors.go

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

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

在這個(gè)基礎(chǔ)上,如果方法返回的是 interface 的時(shí)候,其實(shí)就等于是 簡(jiǎn)單工廠模式,然后再加一層抽象的話,就接近于 抽象工廠模式。

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 根據(jù)不同的類型創(chuàng)建對(duì)應(yīng)的解析器
func NewConfig(t ConfigType) ConfigParser {
switch t {
case JsonType:
return newJsonParser()
case YamlType:
return newYamlParser()
default:
return nil
}
}

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

圖片

Go 實(shí)現(xiàn)簡(jiǎn)單工廠模式

對(duì)象池模式

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

package main

import (
"net/http"
"sync"
)

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

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

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

構(gòu)建模式 (Builder)

將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。

如果用傳統(tǒng)的方法實(shí)現(xiàn) 構(gòu)建模式,對(duì)應(yīng)的 Go 語言代碼大致是下面這個(gè)樣子:

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 {
// 具體實(shí)現(xiàn)代碼跳過
return nil
}

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

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

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

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

println(sql)
}

圖片

Go 實(shí)現(xiàn)構(gòu)建模式

上面的代碼中,通過經(jīng)典的鏈?zhǔn)秸{(diào)用來構(gòu)造出具體的 SQL 語句,但是在 Go 語言中,我們一般使用另外一種模式來實(shí)現(xiàn)同樣的功能 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() {
// 調(diào)用方代碼
sql := NewSQL(Table("users"),
Columns("username", "password"),
Where("id = 100"),
)

println(sql)
}

圖片

Go FUNCTIONAL OPTIONS 模式

觀察者模式

在對(duì)象間定義一個(gè)一對(duì)多的聯(lián)系性,由此當(dāng)一個(gè)對(duì)象改變了狀態(tài),所有其他相關(guān)的對(duì)象會(huì)被通知并且自動(dòng)刷新。

如果用傳統(tǒng)的方法實(shí)現(xiàn) 觀察者模式,對(duì)應(yīng)的 Go 語言代碼大致是下面這個(gè)樣子:

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 觀察者收到訂閱的時(shí)間回調(diào)
func (o *eventObserver) OnNotify(e Event) {
}

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

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

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

func main() {
// 調(diào)用方代碼
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 實(shí)現(xiàn)觀察者模式

但其實(shí)我們有更簡(jiǎn)潔的方法,直接使用標(biāo)準(zhǔn)庫(kù)中的 sync.Cond 對(duì)象,改造之后的 觀察者模式 代碼大概是這個(gè)樣子:

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() // 等待發(fā)出通知
}
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 // 設(shè)置條件變量
c.L.Unlock()

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

func main() {
cond := sync.NewCond(&sync.Mutex{}) // 創(chuàng)建時(shí)傳入一個(gè)互斥鎖

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

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

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

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

圖片

Go 標(biāo)準(zhǔn)庫(kù)觀察者模式

將代碼改造為 sync.Cond 之后,代碼量更好,結(jié)構(gòu)更簡(jiǎn)潔。

ok/error 模式

在 Go 語言中,經(jīng)常在一個(gè)表達(dá)式返回 2 個(gè)參數(shù)時(shí)使用這種模式:

  • 第 1 個(gè)參數(shù)是一個(gè)值或者 nil
  • 第 2 個(gè)參數(shù)是 true/false 或者 error

在一個(gè)需要賦值的 if 條件語句中,使用這種模式去檢測(cè)第 2 個(gè)參數(shù)值會(huì)讓代碼顯得優(yōu)雅簡(jiǎn)潔。

在函數(shù)返回時(shí)檢測(cè)錯(cuò)誤

package main

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

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

檢測(cè) 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)
}
}

檢測(cè)通道是否關(guān)閉

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

附加內(nèi)容

閉包

有時(shí)候,我們可以利用 閉包 實(shí)現(xiàn)一些短小精悍的內(nèi)部函數(shù)。

計(jì)數(shù)器

package main

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

seq := newSeqInc() // 創(chuàng)建一個(gè)計(jì)數(shù)器
println(seq()) // 1
println(seq()) // 2
println(seq()) // 3

seq2 := newSeqInc() // 創(chuàng)建另一個(gè)計(jì)數(shù)器
println(seq2()) // 1
println(seq2()) // 2
println(seq2()) // 3
}

小結(jié)

下面表格列出了常用的 設(shè)計(jì)模式,其中 Go 標(biāo)準(zhǔn)庫(kù)自帶的 模式 已經(jīng)用刪除線標(biāo)識(shí),讀者可以和自己常用的 設(shè)計(jì)模式 進(jìn)行對(duì)比。

創(chuàng)建型模式

結(jié)構(gòu)性模式

行為型模式

單例

適配器

策略

簡(jiǎn)單工廠

裝飾者

觀察者

抽象工廠

代理

狀態(tài)

對(duì)象池


責(zé)任鏈

構(gòu)建



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

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

擴(kuò)展閱讀

  •  Go 與面向?qū)ο?/li>
  • 設(shè)計(jì)模式 - 維基百科[2]
  • go-examples-for-beginners/patterns[3]
  • 圣杯與銀彈 · 沒用的設(shè)計(jì)模式[4]
  • tmrts/go-patterns[5]
  • DESIGN PATTERNS in GO[6]
  • 解密“設(shè)計(jì)模式”[7]
  • Go 編程模式 - 酷殼[8]

引用鏈接

[1] 倉(cāng)庫(kù)代碼: https://github.com/duanbiaowu/go-examples-for-beginners/tree/master/patterns

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

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

[4] 圣杯與銀彈 · 沒用的設(shè)計(jì)模式: 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] 解密“設(shè)計(jì)模式”: ??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

責(zé)任編輯:武曉燕 來源: 洋芋編程
相關(guān)推薦

2023-03-27 00:20:48

2023-04-10 09:20:13

設(shè)計(jì)模式訪客模式

2023-05-04 08:47:31

命令模式抽象接口

2024-10-06 12:56:36

Golang策略設(shè)計(jì)模式

2021-11-08 07:41:16

Go流水線編程

2023-01-31 08:48:49

Go語言文件

2021-04-29 09:02:44

語言Go 處理

2021-03-28 20:58:25

Go語言線程

2022-06-22 09:24:30

云原生Go 語言

2023-05-15 08:51:46

解釋器模式定義

2023-10-13 00:00:00

設(shè)計(jì)模式GO語言

2021-07-13 06:44:04

Go語言數(shù)組

2013-05-28 09:43:38

GoGo語言并發(fā)模式

2023-05-26 08:41:23

模式Go設(shè)計(jì)模式

2022-05-04 23:08:36

標(biāo)準(zhǔn)Go應(yīng)用程序

2024-12-13 08:28:45

設(shè)計(jì)模式依賴

2021-10-20 07:18:51

Go語言設(shè)計(jì)

2022-07-19 12:25:29

Go

2024-05-17 08:47:33

數(shù)組切片元素

2024-10-09 10:33:56

Go語言DevOps
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)