Go装饰器实现
1. 目录
-
介绍装饰器在Go中的实现 -
装饰器在http中的使用 -
装饰器实现Pipeline -
Pipeline在框架中的应用
2. 介绍装饰器在Go中的实现
写过Python
的人都知道,它的“糖”很多,尤其在写装饰器的时候非常简单优雅,比如:
from functools import wraps
def decorator(func):
@wraps(func) # @wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。
def decorated(*args, **kwargs):
print("start")
result = func(*args, **kwargs) # 这里直接调用原函数 并没有修改他 只是在调用之前和之后增加了额外的处理逻辑
print("end")
return result
return decorated
@decorator # 用装饰器装饰add 这种@语法糖确实很香啊
def add(x):
return x + x
print(add(4)) # 直接调用add
结果:
start
end
8
其实装饰器是一种非常优秀的设计模式,它将重复的内容抽象出来,赋予一个函数其他的功能,但又不去改变函数自身。使得代码极其简洁,易于维护。
在Go
中如何实现装饰器呢?其实很简单,只需要利用高阶函数就可以实现,看代码:
package main
import (
"fmt"
)
// 为函数类型设置别名提高代码可读性
type add func(int) int
// 求和
func addFunc(a int) int {
return a + a
}
// 通过高阶函数在不侵入原有函数实现的前提下计算函数之和
func decorator(f add) add {
return func(a int) int {
fmt.Println("start")
c := f(a) // 直接调用
fmt.Println("end")
return c // 返回计算结果
}
}
func main() {
a := 4
// 通过修饰器调用求和函数,返回的是一个匿名函数
deco := decorator(addFunc)
// 执行修饰器返回函数
c := deco(a)
fmt.Println(c)
}
结果:
start
end
8
我们可以看到装饰器模式是遵循SOLID设计原则中的开放封闭原则的,即对扩展开放,对修改关闭。
3. 装饰器在http中的使用
import (
"fmt"
"log"
"net/http"
)
//装饰器
func WithServerHeader(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Println("--->WithServerHeader()")
w.Header().Set("Server", "HelloServer")
h(w, r)
}
}
func hello(w http.ResponseWriter, r *http.Request) {
log.Printf("Recieved Request %s from %sn", r.URL.Path, r.RemoteAddr)
fmt.Fprintf(w, "Hello, World! "+r.URL.Path)
}
func main() {
http.HandleFunc("/api/v1/hello", WithServerHeader(hello)) // 装饰器装饰hello
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
这样的写法比较常见哈,我们在写自己的http服务的时候经常这样使用,其实本质上就是一种装饰器模式,它给你带来的便捷不止于此,请往下看装饰器实现Pipeline
。
4. 装饰器实现Pipeline
上面那个http例子,假如现在有多个装饰器都要装饰hello服务,那么写法可能是这样的:
...
func WithAuthCookie(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Println("--->WithAuthCookie()")
cookie := &http.Cookie{Name: "Auth", Value: "Pass", Path: "/"}
http.SetCookie(w, cookie)
h(w, r)
}
}
...
func main() {
http.HandleFunc("/api/v1/hello", WithServerHeader(WithAuthCookie(hello))) // 多个装饰器装饰hello
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
但是没发现吗,这样的写法层层嵌套并不优雅,如果装饰器非常多,那就非常难看,如何避免这种写法呢?有的,请看:
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("/api/v1/hello", Handler(hello,
WithServerHeader, WithBasicAuth, WithDebugLog))
其实这样的方式也就是Pipeline的实现模式
。
5. Pipeline在框架中的应用
我们只看一个框架gin
,gin中的中间件就是用Pipeline
实现的,而Pipeline的实现就是多个装饰器实现的。
type HandlersChain []HandlerFunc
func (c HandlersChain) Last() HandlerFunc {
if length := len(c); length > 0 {
return c[length-1]
}
return nil
}
//设置中间件
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
group.Handlers = append(group.Handlers, middleware...)
return group.returnObj()
}
因为中间件也是HandlerFunc, 可以当作一个handler来处理。
– END –
原文始发于微信公众号(堆栈future):Go装饰器实现
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/103543.html