Random walk to my blog

my blog for sharing my knowledge,experience and viewpoint

0%

Golang的修饰器模式

装饰器

装饰器(decorator)是一个这样的函数:它的参数是具体类型的函数,并且返回值也是和参数相同类型的函数。
看下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type StringOperator func(string) string

func ident(s string) string {
return s
}

func ToUpper(m StringOperator) StringOperator {
return func(s string) string {
lower := strings.ToUpper(s)
return m(lower)
}
}

func ToMd5(m StringOperator) StringOperator {
return func(s string) string {
h := md5.New()
h.Write([]byte(s))
b64 := base64.StdEncoding.EncodeToString(h.Sum(nil))
return m(b64)

}
}

ToUpperToMd5都接受func(string) string作为参数,并且返回和参数相同的类型func(string) string
调用情况:

1
2
3
4
5
6
7
8
9
10
11
func TestDecorator1(t *testing.T) {
s := "Hello, World"

var fn1 StringOperator = ident
fn1 = ToMd5(ToUpper(ident))
fmt.Println(fn1(s))

var fn2 StringOperator = ident
fn2 = ToUpper(ToMd5(fn2))
fmt.Println(fn2(s))
}

net/httphttp.HandleFunc也用到了装饰器模式。

1
type HandlerFunc func(ResponseWriter, *Request)

调用者可以自己设置http的调用链。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World! "+r.URL.Path)
}

func WithLog(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Printf("Recieved Request %s from %s\n", r.URL.Path, r.RemoteAddr)
h(w, r)
}
}

func TestHttp(t *testing.T) {
http.HandleFunc("/hello", WithLog(hello))
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}

装饰器的流水线(Pipeline)

有时候,多层的调用可能会导致代码不好阅读,例如ToMd5(ToUpper(ident))。这时候可以改成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type Decorator func(StringOperator) StringOperator

func Handler(m StringOperator, decorators ...Decorator) StringOperator {
for i := len(decorators) - 1; i > 0; i-- {
m = decorators[i](m)
}
return m
}

func TestDecorator(t *testing.T) {
s := "Hello, World"
fn := Handler(ident, ToUpper, ToMd5)

fmt.Println(fn(s))
}

设置默认参数

Golang中,函数不支持设置默认参数,可以使用类似装饰器的方法来设置。
看下面的例子,通过WithNumWithString来指定参数。

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
35
36
37
38
39
40
41
42
43
44
type Param struct {
p1 int
p2 OptionParam
}

type OptionParam struct {
a int
b string
}

func defaultOptionParam() OptionParam {
option := OptionParam{
a: 10,
b: "const",
}
return option
}

type SetOption func(option *OptionParam)

func WithNum(num int) SetOption {
return func(option *OptionParam) {
option.a = num
}
}

func WithString(str string) SetOption {
return func(option *OptionParam) {
option.b = str
}
}

func SetParams(p1 int, setOptions ...SetOption) Param {
option := defaultOptionParam() // set default value in the beginning
// custom
for _, set := range setOptions {
set(&option)
}

return Param{
p1: p1,
p2: option,
}
}

我的公众号:lyp_share

我的知乎专栏

我的博客com

我的博客cn