装饰器模式#

装饰器模式可以在不修改原有对象的基础上,通过创建一个包装对象来扩展其功能。这种模式创建了一个装饰类,用来包装原有类,并在保持原类方法签名完整性的前提下提供了额外的功能。

装饰器模式的优点:

  1. 扩展性:可以在不修改原有对象代码的情况下,通过装饰类来扩展其功能。
  2. 灵活性:可以动态地为对象添加功能。
  3. 符合开闭原则:对扩展开放,对修改封闭。

引入#

现在我们有这么一个代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func HelloWorld(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("Hello, world!"))
}

func HowAreYour(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("How are you?"))
}

func main() {
mux := http.NewServeMux()

mux.HandleFunc("/hello", HelloWorld)
mux.HandleFunc("/howareyou", HowAreYour)

srv := &http.Server{
Addr: ":8080",
Handler: mux,
}

_ = srv.ListenAndServe()
}

当我们需要为每个路由都实现一个记录日志的操作:

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
func HelloWorld(w http.ResponseWriter, r *http.Request) {
log.Printf("Hello, world!")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("Hello, world!"))
}

func HowAreYour(w http.ResponseWriter, r *http.Request) {
log.Printf("How are you?")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("How are you?"))
}

func main() {
mux := http.NewServeMux()

mux.HandleFunc("/hello", HelloWorld)
mux.HandleFunc("/howareyou", HowAreYour)

srv := &http.Server{
Addr: ":8080",
Handler: mux,
}

_ = srv.ListenAndServe()
}

但是当路由增多的时候,我们就不得不为每个路由都添加上这么一行重复的代码,非常麻烦,因此我们需要使用装饰器模式来简洁我们的代码

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
type Handler func(w http.ResponseWriter, r *http.Request)

func Logger(handler Handler) Handler {
return func(w http.ResponseWriter, r *http.Request) {
now := time.Now()
handler(w, r)
log.Println("Request processed in ", time.Since(now))
}
}

func HelloWorld(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("Hello, world!"))
}

func HowAreYour(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("How are you?"))
}

func main() {
mux := http.NewServeMux()

mux.HandleFunc("/hello", Logger(HelloWorld))
mux.HandleFunc("/howareyou", Logger(HowAreYour))

srv := &http.Server{
Addr: ":8080",
Handler: mux,
}

_ = srv.ListenAndServe()
}

中间件#

如果我们想要实现一个简单的中间件,我们可以写成这样:

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
func Logger(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
time.Now()
next.ServeHTTP(w, r)
fmt.Println("url: ", r.URL.Path)
}
return http.HandlerFunc(fn)
}

func HelloWorld(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("Hello, world!"))
}

func HowAreYour(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("How are you?"))
}

func main() {
mux := http.NewServeMux()

mux.HandleFunc("/hello", HelloWorld)
mux.HandleFunc("/howareyou", HowAreYour)

srv := &http.Server{
Addr: ":8080",
Handler: Logger(mux),
}

_ = srv.ListenAndServe()
}

Gin#


下面的东西仅供参考:

在 Go 语言中,中间件通常用于处理 HTTP 请求和响应的前置和后置操作。虽然 Go 语言没有像 Python 或 Java 那样的显式装饰器模式,但是可以通过组合和接口来实现类似的功能。

以下是如何在 Go 语言中实现中间件的装饰器模式的步骤:

1. 定义 Handler 接口#

首先,定义一个 Handler 接口,它包含处理 HTTP 请求的方法。

go

1
2
3
4
5
6
7
8
9
10
package main

import (
"net/http"
)

// Handler 接口定义了处理 HTTP 请求的方法
type Handler interface {
ServeHTTP(w http.ResponseWriter, r *http.Request)
}

2. 创建具体 Handler#

实现一个具体的 Handler,它将处理实际的请求。

go

1
2
3
4
5
6
// ConcreteHandler 是一个具体的 Handler 实现
type ConcreteHandler struct{}

func (h *ConcreteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Handling request..."))
}

3. 创建装饰器接口#

定义一个装饰器接口,它包含一个接受 Handler 并返回新 Handler 的方法。

go

1
2
3
4
// Decorator 接口定义了装饰器的方法
type Decorator interface {
Decoration(h Handler) Handler
}

4. 实现具体装饰器#

创建具体的装饰器,它们将包装 Handler 并添加额外的功能。

go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// LoggingDecorator 是一个具体的装饰器,用于添加日志记录功能
type LoggingDecorator struct{}

func (d *LoggingDecorator) Decoration(h Handler) Handler {
return &loggingHandler{h}
}

type loggingHandler struct {
Handler
}

func (h *loggingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
println("Before handling request")
h.Handler.ServeHTTP(w, r)
println("After handling request")
}

5. 使用装饰器#

组合具体的 Handler 和装饰器来创建最终的 Handler。

go

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
// 创建具体的 Handler
handler := &ConcreteHandler{}

// 创建装饰器并装饰 Handler
decorator := &LoggingDecorator{}
finalHandler := decorator.Decoration(handler)

// 创建 HTTP 服务器并设置路由
http.Handle("/", finalHandler)
println("Starting server on :8080")
http.ListenAndServe(":8080", nil)
}

完整代码#

go

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
45
46
47
48
49
50
51
52
53
54
package main

import (
"fmt"
"net/http"
)

// Handler 接口定义了处理 HTTP 请求的方法
type Handler interface {
ServeHTTP(w http.ResponseWriter, r *http.Request)
}

// ConcreteHandler 是一个具体的 Handler 实现
type ConcreteHandler struct{}

func (h *ConcreteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Handling request..."))
}

// Decorator 接口定义了装饰器的方法
type Decorator interface {
Decoration(h Handler) Handler
}

// LoggingDecorator 是一个具体的装饰器,用于添加日志记录功能
type LoggingDecorator struct{}

func (d *LoggingDecorator) Decoration(h Handler) Handler {
return &loggingHandler{h}
}

type loggingHandler struct {
Handler
}

func (h *loggingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Println("Before handling request")
h.Handler.ServeHTTP(w, r)
fmt.Println("After handling request")
}

func main() {
// 创建具体的 Handler
handler := &ConcreteHandler{}

// 创建装饰器并装饰 Handler
decorator := &LoggingDecorator{}
finalHandler := decorator.Decoration(handler)

// 创建 HTTP 服务器并设置路由
http.Handle("/", finalHandler)
fmt.Println("Starting server on :8080")
http.ListenAndServe(":8080", nil)
}

在这个例子中,ConcreteHandler 是一个具体的 Handler,LoggingDecorator 是一个装饰器,它添加了日志记录功能。通过组合装饰器和 Handler,我们可以动态地为 Handler 添加新的行为。

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
return func(c *Context) {
// Start timer
start := time.Now()
path := c.Request.URL.Path
raw := c.Request.URL.RawQuery

// Process request
c.Next()

// Log only when it is not being skipped
if _, ok := skip[path]; ok || (conf.Skip != nil && conf.Skip(c)) {
return
}

param := LogFormatterParams{
Request: c.Request,
isTerm: isTerm,
Keys: c.Keys,
}

// Stop timer
param.TimeStamp = time.Now()
param.Latency = param.TimeStamp.Sub(start)

param.ClientIP = c.ClientIP()
param.Method = c.Request.Method
param.StatusCode = c.Writer.Status()
param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String()

param.BodySize = c.Writer.Size()

if raw != "" {
path = path + "?" + raw
}

param.Path = path

fmt.Fprint(out, formatter(param))
}

}