GO語言的修飾器程式設計

NO IMAGE
1 Star2 Stars3 Stars4 Stars5 Stars 給文章打分!
Loading...

之前寫過一篇《Python修飾器的函數語言程式設計》,這種模式很容易的可以把一些函式裝配到另外一些函式上,可以讓你的程式碼更為的簡單,也可以讓一些“小功能型”的程式碼複用性更高,讓程式碼中的函式可以像樂高玩具那樣自由地拼裝。所以,一直以來,我對修飾器decoration這種程式設計模式情有獨鍾,這裡寫一篇Go語言相關的文章。

看過Python修飾器那篇文章的同學,一定知道這是一種函數語言程式設計的玩法——用一個高階函式來包裝一下。多嘮叨一句,關於函數語言程式設計,可以參看我之前寫過一篇文章《函數語言程式設計》,這篇文章主要是,想通過從程序式程式設計的思維方式過渡到函數語言程式設計的思維方式,從而帶動更多的人玩函數語言程式設計,所以,如果你想了解一下函數語言程式設計,那麼可以移步先閱讀一下。所以,Go語言的修飾器程式設計模式,其實也就是函數語言程式設計的模式。

不過,要提醒注意的是,Go 語言的“糖”不多,而且又是強型別的靜態無虛擬機器的語言,所以,無法做到像 Java 和 Python 那樣的優雅的修飾器的程式碼。當然,也許是我才才疏學淺,如果你知道有更多的寫法,請你一定告訴我。先謝過了。

簡單示例

我們先來看一個示例:

package main
import "fmt"
func decorator(f func(s string)) func(s string) {
        return func(s string) {
                fmt.Println("Started")
                f(s)
                fmt.Println("Done")
        }
}
func Hello(s string) {
        fmt.Println(s)
}
func main() {
        decorator(Hello)("Hello, World!")
}

 

這個玩法和 Python 的異曲同工,只不過,有些遺憾的是,Go 並不支援像 Python 那樣的 @decorator

1)有兩個 Sum 函式,Sum1()

我們再來看一個處理 HTTP 請求的相關的例子。HTTP 相關的一個示例

先看一個簡單的 HTTP Server 的程式碼。

package main
import (
        "fmt"
        "log"
        "net/http"
        "strings"
)
func WithServerHeader(h http.HandlerFunc) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
                log.Println("--->WithServerHeader()")
                w.Header().Set("Server", "HelloServer v0.0.1")
                h(w, r)
        }
}
func hello(w http.ResponseWriter, r *http.Request) {
        log.Printf("Recieved Request %s from %s\n", r.URL.Path, r.RemoteAddr)
        fmt.Fprintf(w, "Hello, World! " r.URL.Path)
}
func main() {
        http.HandleFunc("/v1/hello", WithServerHeader(hello))
        err := http.ListenAndServe(":8080", nil)
        if err != nil {
                log.Fatal("ListenAndServe: ", err)
        }
}

於是,這樣的函式我們可以寫出好些個。如下所示,有寫 HTTP 響應頭的,有寫認證 Cookie 的,有檢查認證Cookie的,有打日誌的……上面程式碼中使用到了修飾模式,WithServerHeader()

在使用上,需要對函式一層層的套起來,看上去好像不是很好看,如果需要 decorator 比較多的話,程式碼會比較難看了。嗯,我們可以重構一下。多個修飾器的 Pipeline

重構時,我們需要先寫一個工具函式——用來遍歷並呼叫各個 decorator:

type HttpHandlerDecorator func(http.HandlerFunc) http.HandlerFunc
func Handler(h http.HandlerFunc, decors ...HttpHandlerDecorator) http.HandlerFunc {
        for i := range decors {
                d := decors[len(decors)-1-i] // iterate in reverse
                h = d(h)
        }
        return h
}

 

然後,我們就可以像下面這樣使用了。

http.HandleFunc("/v4/hello", Handler(hello,
                WithServerHeader, WithBasicAuth, WithDebugLog))

泛型的修飾器這樣的程式碼是不是更易讀了一些?pipeline 的功能也就出來了。

不過,對於 Go 的修飾器模式,還有一個小問題 —— 好像無法做到泛型,就像上面那個計算時間的函式一樣,其程式碼耦合了需要被修飾的函式的介面型別,無法做到非常通用,如果這個事解決不了,那麼,這個修飾器模式還是有點不好用的。

因為 Go 語言不像 Python 和 Java,Python是動態語言,而 Java 有語言虛擬機器,所以他們可以幹好些比較變態的事,然而 Go 語言是一個靜態的語言,這意味著其型別需要在編譯時就要搞定,否則無法編譯。不過,Go 語言支援的最大的泛型是 interface{}

上面這個 Decorator()

 

然後,我們可以這樣做:

type MyFoo func(int, int, int) int
var myfoo MyFoo
Decorator(&myfoo, foo)
myfoo(1, 2, 3)

嗯。如果你不想宣告函式簽名,那麼你也可以這樣你會發現,使用 Decorator()

Again, 如果你有更好的寫法,請你一定要告訴我。好吧,看上去不是那麼的漂亮,但是 it works。看樣子 Go 語言目前本身的特性無法做成像 Java 或 Python 那樣,對此,我們只能多求 Go 語言多放糖了!

程式語言 最新文章