本篇博客的视频教程首发于 Youtube:科技小飞哥,加入 电报粉丝群 获得最新视频更新和问题解答。
今天我们来介绍decorator这个经典的设计模式。
定义:装饰器模式主要对现有的类对象进行包裹和封装,以期望在不改变类对象及其类定义的情况下,为对象添加额外功能。是一种对象结构型模式。
Python装饰器模式
如果你熟悉Python,对装饰器模式应该不陌生。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
def wrapper(method):
def inner(a, b):
print("before...")
ret = method(a, b)
print("after...")
return ret
return inner
@wrapper
def add(a, b):
print("run add")
return a + b
ret = add(1,2)
//ret = wrapper(add)(1,2)
print(ret)
|
这就是最简单的装饰器模式,可以看到,在不改变add的定义的情况下,使用wrapper对add进行装饰。最终会输出:
before...
run add
after...
3
如果去掉line#15, 把line#20替换为line#21,其实效果是一样的,Python装饰器语法糖@wrapper, 在定义的时候就可以装饰函数,这样调用的时候就可以省略掉装饰函数。
Golang装饰器模式
在Golang中,装饰器(decorator)是一个这样的函数:它的参数是具体类型的函数,在装饰函数里面执行具体的函数之余,执行其他操作。
简单示例:
我们先来看一个最简单的示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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的装饰器一模一样,只不过Python支持@decorator语法糖,可以简化调用过程中的decorator。
这里你也可以这么写:
1
2
|
hello := decorator(Hello)
hello("Hello")
|
decorator是一个高阶函数,输入参数是一个函数,输出参数也是一个函数。就是装饰一个函数的作用,在不改变被装饰的函数的前提下,执行一些其他的操作。
这个示例看起来没什么作用,只是示范用,下面举几个在实际开发过程中常用的案例。
计算运行时间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package main
import (
"fmt"
"reflect"
"runtime"
"time"
)
type WrapFunc func(int64, int64) int64
func getFunctionName(i interface{}) string {
return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}
func calculateRunTime(f WrapFunc) WrapFunc {
return func(start, end int64) int64 {
defer func(t time.Time) {
fmt.Printf("--- Time Elapsed (%s): %v ---\n",
getFunctionName(f), time.Since(t))
}(time.Now())
return f(start, end)
}
}
func Add(a, b int64) int64 {
time.Sleep(100 * time.Millisecond)
return a + b
}
func main() {
Add := calculateRunTime(Add)
Add(1, 2)
}
|
可以看到我们有任何一个函数Add, 需要计算函数执行时间,又不想修改函数本身,此时定义一个装饰器函数calculateRunTime,来修饰需要统计的函数Add。
HTTP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
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)
}
}
|
上面代码中使用到了修饰模式,WithServerHeader() 函数就是一个 Decorator,其传入一个 http.HandlerFunc,然后返回一个改写的版本。上面的例子还是比较简单,用 WithServerHeader() 就可以加入一个 Response 的 Header。