欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Go語言如何對數(shù)據(jù)庫進行CRUD操作

 更新時間:2023年05月21日 15:50:49   作者:LightSaid  
在這篇文章中,主要帶大家來學習一下在Go語言中如何對數(shù)據(jù)庫進行CRUD操作,從而探討一下Go的接口編程,感興趣的小伙伴可以跟隨小編一起學習一下

Go語言中的接口

在這里我將不會介紹接口基礎知識,如:接口定義和實現(xiàn)、空接口、類型斷言、v, ok := i.(T)、switch x.(type)、接口嵌套、指針接收器與值接收器實現(xiàn)接口、接口零值 這些基礎概念不作多說,如不太清楚上面接口知識,自行學習補充。這里介紹在開發(fā)中接口實在用法,也是由上面基礎演變而成。

使用接口編程

有這么一個普通場景,上傳并保存文件,三七二十一蹭蹭蹭,一下子搞完,so easy,代碼如下:

項目準備上線,公司購買了OSS存儲,文件資源要存到OSS上,那么如何修改上面的代碼呢? 這個時候接口就可以上場了。 實現(xiàn)如下:

// NOTE: storage.go
// 存儲文件接口
type Storage interface {
    Save(data []byte) (string, error)
}
// 開發(fā)時候使用本地存儲
type localStorage struct {
    config *struct{ base string }
}
func NewLocalStorage(cfg struct{ base string }) *localStorage {
    return &localStorage{config: &cfg}
}
func (store localStorage) Save(data []byte) (string, error) {
    return "http://127.0.0.1/" + store.config.base + "/a.png", nil
}
// 生產(chǎn)使用oss存儲
type ossStorage struct{}
func NewOSSStorage() *ossStorage {
    return &ossStorage{}
}
func (store ossStorage) Save(data []byte) (string, error) {
    return "https://abc.com/oss/a.png", nil
}
// NOTE: upload.go
// 保存文件
func saveFile(store Storage) {
    var data []byte // 偽代碼
    url, _ := store.Save(data)
    fmt.Println(url)
}
func main() {
    var store Storage
    if os.Getenv("ENV") == "prod" {
        store = NewOSSStorage()
    } else {
        store = NewLocalStorage(struct{ base string }{base: "/static"})
	}
    saveFile(store)
}

上面定義Storage接口,localStorage、ossStorage 都是實現(xiàn)了接口,而 saveFile(store Storage) 接受指針。 這種方式就是接受接口返回結構,是 Go 語言接口編程中非常經(jīng)典實用模式。

接受接口返回結構

上面已經(jīng)的例子就是使用這個法則,使用這個法則讓代碼變得更加靈活,它是松耦合的,更加方便mock數(shù)據(jù)測試,可以避免一些不必要的bug。

現(xiàn)在舉一個不方便測試的例子:

type Repository struct {
    DB *sql.DB
}
func NewRepository(db *sql.DB) *Repository {
    return &Repository{DB: db}
}
func (repo *Repository) Insert() {
    // do some thing
}
func (repo *Repository) Update() {
    // do some thing
}
type Service struct {
    Repo Repository
}
func NewService(db *sql.DB) *Service {
    return &Service{
        Repo: *NewRepository(db),
    }
}
func main() {
    // 假設是真實的鏈接 &sql.DB{}
    srv := NewService(&sql.DB{})
    srv.Repo.Insert()
    srv.Repo.Update()
}

上面的例子看著無任何問題???這不很正常。是的很正常,就是測試的時候不方便。 如果要測試起來,你必須要創(chuàng)建一個數(shù)據(jù)庫,連接數(shù)據(jù)庫,創(chuàng)建sql.DB實例。才能完成測試。如果想簡單mock數(shù)據(jù),就不好辦了。

因此還是建議,使用接口編程。修改如下:

type Repository interface {
	Insert()
	Update()
}

type repository struct {
	DB *sql.DB
}

func NewRepository(db *sql.DB) *repository {
	return &repository{DB: db}
}

func (repo *repository) Insert() {
	// do some thing
}

func (repo *repository) Update() {
	// do some thing
}

type Service struct {
	Repo Repository
}

func NewService(r Repository) *Service {
	return &Service{
		Repo: r,
	}
}

func main() {
	// 假設是真實的鏈接 &sql.DB{}
	r := NewRepository(&sql.DB{})
	srv := NewService(r)
	srv.Repo.Insert()
	srv.Repo.Update()
}

接口檢查

上面的例子,func NewRepository(db *sql.DB) *repository 返回的是結構體指針。在編碼過程,很容易忘記實現(xiàn)接口,甚至全部接口都沒實現(xiàn),編譯器也不會報錯。我怎么知道repository結構體就一定全部實現(xiàn)了Repository接口呢,對吧。

那這個時候就要用到接口檢查了。 接口檢查的語法是

var _ MyInterface = (*MyStruct)(nil)

上面當我沒有實現(xiàn)Inser方法時就會直接編譯不通過。

這就是接口檢查帶來好處。

擴展接口

假設有這么一場景,在開發(fā)中我們使用到了第三方庫,第三庫提供一個Worker接口,我們在work、next兩個方法都使用到Worker接口,入?yún)⒕褪撬?。這個時候我們希望在work函數(shù)中對Context增加額外的屬性,提供給next函數(shù)使用。但現(xiàn)實是沒法直接通過context.WithValue(w.Context(), "greet", "Hello")設置值的,因為Worker接口僅返回Context并重新沒有設置Context方法提供;由于Worker是第三方的接口,不可直接去改的。因此就需要擴展接口。問題如圖:

如何解決這個問題呢?現(xiàn)在要解決什么問題?如果把問題細分的話,要解決2個問題:

  • 擴展一個設置Context的方法
  • 保留原來的方法,只做擴充,不做刪除 那要解決這個問題最好的方式就是接口嵌套。

代碼該如何寫呢?解法如下:

首先是要定義新的接口wrapWorker,將舊的接口嵌套進來,這樣就保留了原始有的方法,然后在定義新的方法SetContext(context.Context),接口定義好了,就用實現(xiàn)接口對吧,思路很直接。

緊接著定義一個 wrap 結構體, 保存ctx,同時實SetContext(context.Context)

還有重要的一環(huán)節(jié),就是重寫 Context() context.Context 方法。

實現(xiàn)代碼如下:

// 第三方的接口定義
type Worker interface {
    Context() context.Context
    DoSomeThing()
    // ...
}

type wrapWorker interface {
    Worker
    SetContext(context.Context)
}
type wrap struct {
    Worker
    ctx context.Context
}

func newWrap(w Worker) wrapWorker {
    return &wrap{
        w,
        w.Context(),
    }
}
func (wp *wrap) SetContext(ctx context.Context) {
    wp.ctx = ctx
}

func (wp wrap) Context() context.Context {
    return wp.ctx
}

type contextKey string

func work(w Worker) {
    wp := newWrap(w)
    ctx := context.WithValue(w.Context(), contextKey("greet"), "Hello")
    wp.SetContext(ctx)

    next(wp)
}

func next(w Worker) {
    v := w.Context().Value(contextKey("greet"))
    fmt.Printf("-> %v \n", v)
    w.DoSomeThing()
}

type person string

func (person) Context() context.Context {
    return context.Background()
}
func (p person) DoSomeThing() {
    fmt.Println(string(p), "吃飯睡覺打代碼~")
}

func main() {
    var p person = "張三"
    work(p)
}

執(zhí)行結果符合預期,OK 沒問題~

其實上面不定義新接口wrapWorker也是可以的,用wrap包裹接口即可。用接口設計更符合接口編程思想。

到此這篇關于詳解Go語言如何對數(shù)據(jù)庫進行CRUD操作的文章就介紹到這了,更多相關Go語言數(shù)據(jù)庫CRUD操作內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Golang defer延遲語句的實現(xiàn)

    Golang defer延遲語句的實現(xiàn)

    defer擁有注冊延遲調(diào)用的機制,本文主要介紹了Golang defer延遲語句的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-07-07
  • Go?語言入門學習之時間包

    Go?語言入門學習之時間包

    這篇文章主要介紹了Go?語言入門學習之時間包,GO?語言提供了???time??包來測量和顯示時間,下文關于GO時間包的相關介紹需要的小伙伴可以參考一下
    2022-04-04
  • golang中channel+error來做異步錯誤處理有多香

    golang中channel+error來做異步錯誤處理有多香

    官方推薦golang中錯誤處理當做值處理, 既然是值那就可以在channel中傳輸,這篇文章主要介紹了golang 錯誤處理channel+error真的香,需要的朋友可以參考下
    2023-01-01
  • Go語言底層原理互斥鎖的實現(xiàn)原理

    Go語言底層原理互斥鎖的實現(xiàn)原理

    這篇文章主要介紹了Go語言底層原理互斥鎖的實現(xiàn)原理,Go?sync包提供了兩種鎖類型,分別是互斥鎖sync.Mutex和讀寫互斥鎖sync.RWMutex,都屬于悲觀鎖,更多相關內(nèi)容需要的朋友可以查看下面文章內(nèi)容
    2022-08-08
  • Go語言map實現(xiàn)順序讀取

    Go語言map實現(xiàn)順序讀取

    當我們遍歷 map 時,那就是輸出的鍵值對順序是不確定的,本文主要介紹了Go語言map實現(xiàn)順序讀取, 文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-05-05
  • 使用Go語言簡單模擬Python的生成器

    使用Go語言簡單模擬Python的生成器

    這篇文章主要介紹了使用Go語言簡單模擬Python的生成器,Python的generator是非??岬墓δ?用Go實現(xiàn)的代碼也較為簡潔,需要的朋友可以參考下
    2015-08-08
  • Go語言常見錯誤之濫用getters/setters誤區(qū)實例探究

    Go語言常見錯誤之濫用getters/setters誤區(qū)實例探究

    在Go語言編程中,恰如其分地使用getters和setters是至關重要的,過度和不適當?shù)厥褂盟鼈兛赡軐е麓a冗余、可讀性差和封裝不當,在本文中,我們將深入探討如何識別濫用getter和setter的情況,以及如何采取最佳實踐來避免這些常見的Go錯誤
    2024-01-01
  • Golang 探索對Goroutine的控制方法(詳解)

    Golang 探索對Goroutine的控制方法(詳解)

    下面小編就為大家分享一篇Golang 探索對Goroutine的控制方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-12-12
  • Golang中context庫的高級應用

    Golang中context庫的高級應用

    context庫不僅對于提升代碼的效率和性能至關重要,而且還幫助開發(fā)者在復雜的系統(tǒng)中保持代碼的清晰和可維護性,下面我們就來看看context庫的高級應用吧
    2024-01-01
  • go依賴注入庫samber/do使用示例講解

    go依賴注入庫samber/do使用示例講解

    這篇文章主要介紹了go依賴注入庫samber/do使用,在本文中,我們學習了如何使用samber/do在?Go?中提供依賴注入,需要的朋友可以參考下
    2024-02-02

最新評論