最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go語言常見設(shè)計(jì)模式之裝飾模式詳解

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

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

簡(jiǎn)單裝飾器

首先我們編寫一個(gè)簡(jiǎn)單的 hello 函數(shù):

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

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

現(xiàn)在我們想在打印 Hello World! 前后各加一行日志,最直接的實(shí)現(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

更好的實(shí)現(xiàn)方式是單獨(dú)編寫一個(gè) 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ù),并且返回一個(gè)函數(shù),而且參數(shù)和返回值的函數(shù)簽名同 hello 函數(shù)一樣。我們將原來調(diào)用 hello() 的地方改成:

hello := logger(hello)
hello()

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

before
Hello World!
after

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

logger 函數(shù)也就是我們?cè)?Python 中經(jīng)常使用的裝飾器,如果按照 Python 中裝飾器的寫法,我們可以這樣做:

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

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

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

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

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

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...) 的方式給路由增加非常多的中間件,這樣我們就能夠很方便的攔截路由處理函數(shù),并在其前后分別做一些處理邏輯。如上面示例中使用 gin.Logger() 增加日志,使用 gin.Recovery() 來處理 panic 異常。

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

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

這是一個(gè)簡(jiǎn)單的 Web Server 程序,其監(jiān)聽 8888 端口,當(dāng)訪問 /hello 路由時(shí)會(huì)進(jìn)入 handleHello 函數(shù)邏輯。

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

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

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

before
handle hello
after

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

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

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

接下來我們要做的就是將 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ù)將 handlermiddleware 聚合到一起,實(shí)現(xiàn)了讓這個(gè)簡(jiǎn)單的 Web Server 中間件用法跟 Gin 框架用法相似的效果。

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

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

總結(jié)

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

盡管我們最終實(shí)現(xiàn)的 pipelineHandlers 不如 Gin 框架中間件強(qiáng)大,比如不能延遲調(diào)用,通過 c.Next() 控制中間件調(diào)用流等,但通過這個(gè)簡(jiǎn)單的示例,相信對(duì)你接下來深入學(xué)習(xí) Gin 框架會(huì)有所幫助。

以上就是Go語言常見設(shè)計(jì)模式之裝飾模式詳解的詳細(xì)內(nèi)容,更多關(guān)于Go裝飾模式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 舉例講解Go語言中函數(shù)的閉包使用

    舉例講解Go語言中函數(shù)的閉包使用

    這篇文章主要介紹了Go語言中函數(shù)的閉包使用示例,函數(shù)閉包c(diǎn)losure是編程語言中十分重要的特性,需要的朋友可以參考下
    2016-03-03
  • Hugo 游樂場(chǎng)內(nèi)容初始化示例詳解

    Hugo 游樂場(chǎng)內(nèi)容初始化示例詳解

    這篇文章主要為大家介紹了Hugo 游樂場(chǎng)內(nèi)容初始化示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • golang編程入門之http請(qǐng)求天氣實(shí)例

    golang編程入門之http請(qǐng)求天氣實(shí)例

    這篇文章主要介紹了golang編程入門之http請(qǐng)求天氣實(shí)例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-08-08
  • Golang通道的無阻塞讀寫的方法示例

    Golang通道的無阻塞讀寫的方法示例

    這篇文章主要介紹了Golang通道的無阻塞讀寫的方法示例,詳細(xì)的介紹了哪些情況會(huì)存在阻塞,以及如何使用select解決阻塞,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2018-11-11
  • Golang實(shí)現(xiàn)文件傳輸功能

    Golang實(shí)現(xiàn)文件傳輸功能

    這篇文章主要為大家詳細(xì)介紹了Golang實(shí)現(xiàn)文件傳輸功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • GO語言求100以內(nèi)的素?cái)?shù)

    GO語言求100以內(nèi)的素?cái)?shù)

    這篇文章主要介紹了GO語言求100以內(nèi)的素?cái)?shù),主要通過篩選法來實(shí)現(xiàn),涉及GO語言基本的循環(huán)與函數(shù)調(diào)用方法,需要的朋友可以參考下
    2014-12-12
  • Golang解析yaml文件操作指南

    Golang解析yaml文件操作指南

    之前一直從事java開發(fā),習(xí)慣了使用yaml文件的格式,尤其是清晰的層次結(jié)構(gòu)、注釋,下面這篇文章主要給大家介紹了關(guān)于Golang解析yaml文件的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-09-09
  • 如何用go-zero 實(shí)現(xiàn)中臺(tái)系統(tǒng)

    如何用go-zero 實(shí)現(xiàn)中臺(tái)系統(tǒng)

    這篇文章主要介紹了如何用go-zero 實(shí)現(xiàn)中臺(tái)系統(tǒng),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • go語言yaml轉(zhuǎn)map、map遍歷的實(shí)現(xiàn)

    go語言yaml轉(zhuǎn)map、map遍歷的實(shí)現(xiàn)

    本文主要介紹了go語言yaml轉(zhuǎn)map、map遍歷的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • 深入探索Go語言中unsafe包的使用

    深入探索Go語言中unsafe包的使用

    Go語言的unsafe包被譽(yù)為黑科技,它為Go語言提供了底層訪問和操控內(nèi)存的能力,本文將深入探討Go語言中unsafe包的使用方法和注意事項(xiàng),需要的可以參考一下
    2023-04-04

最新評(píng)論

浏阳市| 宁海县| 临高县| 云安县| 兰西县| 新和县| 嵊泗县| 东丽区| 大姚县| 保德县| 东平县| 九龙坡区| 龙游县| 公安县| 永年县| 赣州市| 罗定市| 太康县| 泸水县| 酒泉市| 石景山区| 且末县| 衡东县| 大英县| 赫章县| 双峰县| 唐河县| 通渭县| 岱山县| 丁青县| 怀仁县| 杂多县| 邹平县| 博野县| 汝阳县| 隆回县| 平乐县| 丹东市| 青铜峡市| 平昌县| 子长县|