亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Go語言常見設計模式之裝飾模式詳解

 更新時間:2023年07月05日 11:05:35   作者:江湖十年  
在?Go?語言中,雖然裝飾模式沒有像?Python?中應用那么廣泛,但也有其用武之地,這篇文章我們就來一起看下裝飾模式在?Go?語言中的應用吧

熟悉 Python 的同學想必對裝飾模式都不會太陌生,Python 從語法上原生支持裝飾器,大大提高了裝飾模式在 Python 中的應用。而在 Go 語言中,雖然裝飾模式沒有像 Python 中應用那么廣泛,但也有其用武之地,這篇文章我們就來一起看下裝飾模式在 Go 語言中的應用。

簡單裝飾器

首先我們編寫一個簡單的 hello 函數:

package main
import "fmt"
func hello() {
	fmt.Println("Hello World!")
}
func main() {
	hello()
}

上面代碼執(zhí)行后輸出 Hello World!。

現(xiàn)在我們想在打印 Hello World! 前后各加一行日志,最直接的實現(xiàn)方式如下:

package main
import "fmt"
func hello() {
	fmt.Println("before")
	fmt.Println("Hello World!")
	fmt.Println("after")
}
func main() {
	hello()
}

代碼執(zhí)行后輸出:

before
Hello World!
after

更好的實現(xiàn)方式是單獨編寫一個 logger 函數,專門用來打印日志:

package main
import "fmt"
func logger(f func()) func() {
	return func() {
		fmt.Println("before")
		f()
		fmt.Println("after")
	}
}
func hello() {
	fmt.Println("Hello World!")
}
func main() {
	hello := logger(hello)
	hello()
}

logger 函數接收一個函數,并且返回一個函數,而且參數和返回值的函數簽名同 hello 函數一樣。我們將原來調用 hello() 的地方改成:

hello := logger(hello)
hello()

就實現(xiàn)了 logger 函數對 hello 函數的包裝,執(zhí)行后的打印結果仍為:

before
Hello World!
after

這樣我們就以一種更加優(yōu)雅的方式,實現(xiàn)了給 hello 函數增加日志的功能,因為這個 logger 函數不僅可以用于 hello,還可以用于其他任何與 hello 函數有著同樣簽名的函數。

logger 函數也就是我們在 Python 中經常使用的裝飾器,如果按照 Python 中裝飾器的寫法,我們可以這樣做:

package main
import "fmt"
func logger(f func()) func() {
	return func() {
		fmt.Println("before")
		f()
		fmt.Println("after")
	}
}
// 給 hello 函數打上 logger 裝飾器
@logger
func hello() {
	fmt.Println("Hello World!")
}
func main() {
	// hello 函數調用方式不變
	hello()
}

但很遺憾,上面的程序無法通過編譯,Go 語言目前還沒有像 Python 語言一樣從語法層面提供對裝飾器語法糖的支持。

裝飾器實現(xiàn)中間件

盡管 Go 語言中裝飾器的寫法不如 Python 語言精簡,但有一個場景的確非常適用,那就是 Web 開發(fā)場景中的中間件組件。

如果你用過 Gin Web 框架,那么應該很容易能看懂如下代碼:

package main
import "github.com/gin-gonic/gin"
func main() {
	r := gin.New()
	// 使用中間件
	r.Use(gin.Logger(), gin.Recovery())
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})
	_ = r.Run(":8888")
}

Gin 框架中可以通過 r.Use(middlewares...) 的方式給路由增加非常多的中間件,這樣我們就能夠很方便的攔截路由處理函數,并在其前后分別做一些處理邏輯。如上面示例中使用 gin.Logger() 增加日志,使用 gin.Recovery() 來處理 panic 異常。

Gin 框架的中間件正是使用裝飾模式來實現(xiàn)的,我們可以借用 Go 語言自帶的 http 庫來簡單模擬下:

package main
import (
	"fmt"
	"net/http"
)
func loggerMiddleware(f http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("before")
		f(w, r)
		fmt.Println("after")
	}
}
func authMiddleware(f http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if token := r.Header.Get("token"); token != "fake_token" {
			_, _ = w.Write([]byte("unauthorized\n"))
			return
		}
		f(w, r)
	}
}
func handleHello(w http.ResponseWriter, r *http.Request) {
	fmt.Println("handle hello")
	_, _ = w.Write([]byte("Hello World!\n"))
}
func main() {
	http.HandleFunc("/hello", authMiddleware(loggerMiddleware(handleHello)))
	fmt.Println(http.ListenAndServe(":8888", nil))
}

這是一個簡單的 Web Server 程序,其監(jiān)聽 8888 端口,當訪問 /hello 路由時會進入 handleHello 函數邏輯。

我們分別使用 loggerMiddlewareauthMiddleware 函數對 handleHello 進行了包裝,使其支持打印訪問日志和認證校驗功能。假如我們還有其他中間件攔截功能需要加入,就可以這么無限包裝下去。

啟動這個 Server 來驗證下裝飾器:

可以對結果進行簡單分析,第一次請求 /hello 接口時,由于沒有攜帶認證 token,收到了 unauthorized 響應;第二次請求時攜帶了 token,得到響應 Hello World!,并且后臺程序打印如下日志:

before
handle hello
after

說明中間件執(zhí)行順序是先由外向內進入,再由內向外返回。而這種一層一層包裝處理邏輯的模型有一個非常形象的名字,叫作洋蔥模型。

這個名字可以說非常貼切了。但我們用洋蔥模型實現(xiàn)的中間件,相比于 Gin 框架的中間件寫法上還差點意思,這種一層層包裹函數的寫法不如 Gin 框架提供的 r.Use(middlewares...) 寫法直觀。

如果你去看 Gin 框架源碼,就會發(fā)現(xiàn)它的中間件和 handler 處理函數實際上會被一起聚合到路由節(jié)點的 handlers 屬性中,而這個 handlers 屬性其實就是一個 HandlerFunc 類型切片。對應到咱們用 http 標準庫實現(xiàn)的 Web Server 中,就是滿足 func(ResponseWriter, *Request) 類型的 handler 切片。當路由接口被調用時,Gin 框架就會依次執(zhí)行 handlers 切片中的所有函數,整個中間件的調用流程就像一條流水線一樣依次調用,再依次返回。而這種思想也有一個形象的名字,叫作流水線(Pipeline)。

接下來我們要做的就是將 handleHello 和兩個中間件 loggerMiddleware、authMiddleware 聚合到一起,同樣形成一個 Pipeline。

package main
import (
	"fmt"
	"net/http"
)
func authMiddleware(f http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if token := r.Header.Get("token"); token != "fake_token" {
			_, _ = w.Write([]byte("unauthorized\n"))
			return
		}
		f(w, r)
	}
}
func loggerMiddleware(f http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("before")
		f(w, r)
		fmt.Println("after")
	}
}
type handler func(http.HandlerFunc) http.HandlerFunc
// 聚合 handler 和 middleware
func pipelineHandlers(h http.HandlerFunc, hs ...handler) http.HandlerFunc {
	for i := range hs {
		h = hs[i](h)
	}
	return h
}
func handleHello(w http.ResponseWriter, r *http.Request) {
	fmt.Println("handle hello")
	_, _ = w.Write([]byte("Hello World!\n"))
}
func main() {
	http.HandleFunc("/hello", pipelineHandlers(handleHello, loggerMiddleware, authMiddleware))
	fmt.Println(http.ListenAndServe(":8888", nil))
}

我們借用 pipelineHandlers 函數將 handlermiddleware 聚合到一起,實現(xiàn)了讓這個簡單的 Web Server 中間件用法跟 Gin 框架用法相似的效果。

再次啟動 Server 進行驗證:

改造成功,跟之前使用洋蔥模型寫法的結果如出一轍。

總結

在簡單介紹了 Go 語言中如何實現(xiàn)裝飾模式后,我們通過對一個 Web Server 程序中間件的講解,學習了裝飾模式在 Go 語言中的應用。因為 Go 語言是靜態(tài)類型語言,不像 Python 那般靈活,所以在實現(xiàn)上要多費一點力氣,并且 Go 語言實現(xiàn)的裝飾器由于有類型上的限制,也不如 Python 裝飾器那般通用,但仍有用武之地。

盡管我們最終實現(xiàn)的 pipelineHandlers 不如 Gin 框架中間件強大,比如不能延遲調用,通過 c.Next() 控制中間件調用流等,但通過這個簡單的示例,相信對你接下來深入學習 Gin 框架會有所幫助。

以上就是Go語言常見設計模式之裝飾模式詳解的詳細內容,更多關于Go裝飾模式的資料請關注腳本之家其它相關文章!

相關文章

  • golang實現(xiàn)文件上傳并轉存數據庫功能

    golang實現(xiàn)文件上傳并轉存數據庫功能

    這篇文章主要為大家詳細介紹了golang實現(xiàn)文件上傳并轉存數據庫功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • 淺談Go語言中字符串和數組

    淺談Go語言中字符串和數組

    這篇文章主要簡單介紹了Go語言中字符串和數組的使用方法和申明方式,需要的朋友可以參考下
    2015-01-01
  • Golang語言的多種變量聲明方式與使用場景詳解

    Golang語言的多種變量聲明方式與使用場景詳解

    Golang當中的變量類型和C/C++比較接近,一般用的比較多的也就是int,float和字符串,下面這篇文章主要給大家介紹了關于Golang語言的多種變量聲明方式與使用場景的相關資料,需要的朋友可以參考下
    2022-02-02
  • Golang的CSP模型簡介(最新推薦)

    Golang的CSP模型簡介(最新推薦)

    Golang采用了CSP(Communicating?Sequential?Processes,通信順序進程)并發(fā)模型,通過goroutine和channel提供了一種更為簡潔和安全的并發(fā)編程方式,本文將詳細介紹Golang的CSP并發(fā)模型及其使用方法,感興趣的朋友一起看看吧
    2025-01-01
  • Go語言中日志的規(guī)范使用建議分享

    Go語言中日志的規(guī)范使用建議分享

    在任何服務端的語言項目中,日志是至關重要的組成部分,本文為大家整理了一些如何規(guī)范使用GO語言日志的建議,以及相應的實際示例,希望對大家有事幫助
    2024-01-01
  • GO利用channel協(xié)調協(xié)程的實現(xiàn)

    GO利用channel協(xié)調協(xié)程的實現(xiàn)

    本文主要介紹了GO利用channel協(xié)調協(xié)程的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-05-05
  • 一文帶你使用golang手擼一個websocket中間件

    一文帶你使用golang手擼一個websocket中間件

    這篇文章主要為大家詳細介紹了如何使用golang手擼一個websocket中間件,文中的示例代碼講解詳細,具有一定的借鑒價值,感興趣的小伙伴可以參考一下
    2023-12-12
  • Goland中Protobuf的安裝、配置和使用

    Goland中Protobuf的安裝、配置和使用

    本文記錄了mac環(huán)境下protobuf的編譯安裝,并通過一個示例來演示proto自動生成go代碼,本文使用的mac?os?12.3系統(tǒng),不建議使用homebrew安裝,系統(tǒng)版本太高,會安裝報錯,所以自己下載新版壓縮包編譯構建安裝
    2022-05-05
  • Mac下Vs code配置Go語言環(huán)境的詳細過程

    Mac下Vs code配置Go語言環(huán)境的詳細過程

    這篇文章給大家介紹Mac下Vs code配置Go語言環(huán)境的詳細過程,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-07-07
  • 關于go get 下載第三方包存儲路徑問題

    關于go get 下載第三方包存儲路徑問題

    這篇文章主要介紹了關于go get 下載第三方包存儲路徑問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01

最新評論