av激情亚洲男人的天堂国语,日韩欧美精品一中文字幕,无码av一区二区三区无码,国产又色又爽又刺激的a片,国产又色又爽又刺激的a片

Golang 常見(jiàn)設(shè)計(jì)模式之裝飾模式

想必只要是熟悉 Python 的同學(xué)對(duì)裝飾模式一定不會(huì)陌生,這類(lèi) Python 從語(yǔ)法上原生支持的裝飾器,大大提高了裝飾模式在 Python 中的應(yīng)用。盡管 Go 語(yǔ)言中裝飾模式?jīng)]有 Python 中應(yīng)用的那么廣泛,但是它也有其獨(dú)到的地方。接下來(lái)就一起看下裝飾模式在 Go 語(yǔ)言中的應(yīng)用。

創(chuàng)新互聯(lián)是少有的成都網(wǎng)站制作、成都做網(wǎng)站、外貿(mào)營(yíng)銷(xiāo)網(wǎng)站建設(shè)、營(yíng)銷(xiāo)型企業(yè)網(wǎng)站、成都微信小程序、手機(jī)APP,開(kāi)發(fā)、制作、設(shè)計(jì)、外鏈、推廣優(yōu)化一站式服務(wù)網(wǎng)絡(luò)公司,從2013年成立,堅(jiān)持透明化,價(jià)格低,無(wú)套路經(jīng)營(yíng)理念。讓網(wǎng)頁(yè)驚喜每一位訪客多年來(lái)深受用戶好評(píng)

簡(jiǎn)單裝飾器

我們通過(guò)一個(gè)簡(jiǎn)單的例子來(lái)看一下裝飾器的簡(jiǎn)單應(yīng)用,首先編寫(xiě)一個(gè) hello 函數(shù):

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

完成上面代碼后,執(zhí)行會(huì)輸出“Hello World!”。接下來(lái)通過(guò)以下方式,在打印“Hello World!”前后各加一行日志:

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

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

before
Hello World!
after

當(dāng)然我們可以選擇一個(gè)更好的實(shí)現(xiàn)方式,即單獨(dú)編寫(xiě)一個(gè)專(zhuān)門(mén)用來(lái)打印日志的 logger 函數(shù),示例如下:

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 函數(shù)接收并返回了一個(gè)函數(shù),且參數(shù)和返回值的函數(shù)簽名同 hello 一樣。然后我們?cè)谠瓉?lái)調(diào)用 hello() 的位置進(jìn)行如下修改:

hello := logger(hello)
hello()

這樣我們通過(guò) logger 函數(shù)對(duì) hello 函數(shù)的包裝,更加優(yōu)雅的實(shí)現(xiàn)了給 hello 函數(shù)增加日志的功能。執(zhí)行后的打印結(jié)果仍為:

before
Hello World!
after

其實(shí) logger 函數(shù)也就是我們?cè)?Python 中經(jīng)常使用的裝飾器,因?yàn)?logger 函數(shù)不僅可以用于 hello,還可以用于其他任何與 hello 函數(shù)有著同樣簽名的函數(shù)。

當(dāng)然如果想使用 Python 中裝飾器的寫(xiě)法,我們可以這樣做:

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

但很遺憾,上面的程序無(wú)法通過(guò)編譯。因?yàn)?Go 語(yǔ)言目前還沒(méi)有像 Python 語(yǔ)言一樣從語(yǔ)法層面提供對(duì)裝飾器語(yǔ)法糖的支持。

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

盡管 Go 語(yǔ)言中裝飾器的寫(xiě)法不如 Python 語(yǔ)言精簡(jiǎn),但它被廣泛運(yùn)用于 Web 開(kāi)發(fā)場(chǎng)景的中間件組件中。比如 Gin Web 框架的如下代碼,只要使用過(guò)就肯定會(huì)覺(jué)得熟悉:

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.Logger() 增加日志,使用 gin.Recovery() 來(lái)處理 panic 異常一樣,在 Gin 框架中可以通過(guò) r.Use(middlewares...) 的方式給路由增加非常多的中間件,來(lái)方便我們攔截路由處理函數(shù),并在其前后分別做一些處理邏輯。

而 Gin 框架的中間件正是使用裝飾模式來(lái)實(shí)現(xiàn)的。下面我們借用 Go 語(yǔ)言自帶的 http 庫(kù)進(jìn)行一個(gè)簡(jiǎn)單模擬。這是一個(gè)簡(jiǎn)單的 Web Server 程序,其監(jiān)聽(tīng) 8888 端口,當(dāng)訪問(wèn) /hello 路由時(shí)會(huì)進(jìn)入 handleHello 函數(shù)邏輯:

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))
}

我們分別使用 loggerMiddleware、authMiddleware 函數(shù)對(duì) handleHello 進(jìn)行了包裝,使其支持打印訪問(wèn)日志和認(rèn)證校驗(yàn)功能。如果我們還需要加入其他中間件攔截功能,可以通過(guò)這種方式進(jìn)行無(wú)限包裝。

啟動(dòng)這個(gè) Server 來(lái)驗(yàn)證下裝飾器:

對(duì)結(jié)果進(jìn)行簡(jiǎn)單分析可以看到,第一次請(qǐng)求 /hello 接口時(shí),由于沒(méi)有攜帶認(rèn)證 token,收到了 unauthorized 響應(yīng)。第二次請(qǐng)求時(shí)攜帶了 token,則得到響應(yīng)“Hello World!”,并且后臺(tái)程序打印如下日志:

before
handle hello
after

這說(shuō)明中間件執(zhí)行順序是先由外向內(nèi)進(jìn)入,再由內(nèi)向外返回。而這種一層一層包裝處理邏輯的模型有一個(gè)非常形象且貼切的名字,洋蔥模型。

但用洋蔥模型實(shí)現(xiàn)的中間件有一個(gè)直觀的問(wèn)題。相比于 Gin 框架的中間件寫(xiě)法,這種一層層包裹函數(shù)的寫(xiě)法不如 Gin 框架提供的 r.Use(middlewares...) 寫(xiě)法直觀。

Gin 框架源碼的中間件和 handler 處理函數(shù)實(shí)際上被一起聚合到了路由節(jié)點(diǎn)的 handlers 屬性中。其中 handlers 屬性是 HandlerFunc 類(lèi)型切片。對(duì)應(yīng)到用 http 標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)的 Web Server 中,就是滿足 func(ResponseWriter, *Request) 類(lèi)型的 handler 切片。

當(dāng)路由接口被調(diào)用時(shí),Gin 框架就會(huì)像流水線一樣依次調(diào)用執(zhí)行 handlers 切片中的所有函數(shù),再依次返回。這種思想也有一個(gè)形象的名字,就叫作流水線(Pipeline)。

接下來(lái)我們要做的就是將 handleHello 和兩個(gè)中間件 loggerMiddleware、authMiddleware 聚合到一起,同樣形成一個(gè) 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 函數(shù)將 handler 和 middleware 聚合到一起,實(shí)現(xiàn)了讓這個(gè)簡(jiǎn)單的 Web Server 中間件用法跟 Gin 框架用法相似的效果。

再次啟動(dòng) Server 進(jìn)行驗(yàn)證:

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

總結(jié)

簡(jiǎn)單了解了 Go 語(yǔ)言中如何實(shí)現(xiàn)裝飾模式后,我們通過(guò)一個(gè) Web Server 程序中間件,學(xué)習(xí)了裝飾模式在 Go 語(yǔ)言中的應(yīng)用。

需要注意的是,盡管 Go 語(yǔ)言實(shí)現(xiàn)的裝飾器有類(lèi)型上的限制,不如 Python 裝飾器那般通用。就像我們最終實(shí)現(xiàn)的 pipelineHandlers 不如 Gin 框架中間件強(qiáng)大,比如不能延遲調(diào)用,通過(guò) c.Next() 控制中間件調(diào)用流等。但不能因?yàn)檫@樣就放棄,因?yàn)?GO 語(yǔ)言裝飾器依然有它的用武之地。

Go 語(yǔ)言是靜態(tài)類(lèi)型語(yǔ)言不像 Python 那般靈活,所以在實(shí)現(xiàn)上要多費(fèi)一點(diǎn)力氣。希望通過(guò)這個(gè)簡(jiǎn)單的示例,相信對(duì)大家深入學(xué)習(xí) Gin 框架有所幫助。


文章題目:Golang 常見(jiàn)設(shè)計(jì)模式之裝飾模式
鏈接地址:http://uogjgqi.cn/article/djheidj.html
掃二維碼與項(xiàng)目經(jīng)理溝通

我們?cè)谖⑿派?4小時(shí)期待你的聲音

解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流