在Go的世界里有一些特有的设计模式,这些设计模式利用了Go语言的特性,解决了特定的问题,这些设计模式很多Gopher都会如数家珍,我们称之为Go世界里的黑话。
中间件模式(Middleware)
中间件模式本质上是装饰模式(Decorator)在Go里的实现,他利用了Go函数式编程的思想。他可以动态添加一些横向的功能,类似于面向对象里的AOP
go
type Handler func(w http.ResponseWriter, r *http.Request)
func LoggerMiddleware(handler Handler) Handler {
return func(w http.ResponseWriter, r *http.Request) {
log.Printf("[access] %s", r.URL.String())
handler(w, r)
}
}
func PingHandle(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ping"))
}
func HelloHandle(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello"))
}
func main() {
http.HandleFunc("/ping", LoggerMiddleware(PingHandle))
http.HandleFunc("/hello", LoggerMiddleware(HelloHandle))
http.ListenAndServe(":8080", nil)
}
多层中间件
middleware.go
go
package middleware
import "net/http"
type MultiMiddleware struct {
middleware []Middleware
}
type Handler func(w http.ResponseWriter, r *http.Request)
type Middleware func(Handler) Handler
func NewMultiMiddleware() *MultiMiddleware {
return &MultiMiddleware{}
}
func (m *MultiMiddleware) Use(middleware Middleware) {
m.middleware = append(m.middleware, middleware)
}
func (m *MultiMiddleware) Middleware(handler Handler) Handler {
for _, middleware := range m.middleware {
handler = middleware(handler)
}
return handler
}
main.go
go
package main
import (
"log"
"net/http"
"testy/middleware"
)
func LoggerMiddleware(handler middleware.Handler) middleware.Handler {
return func(w http.ResponseWriter, r *http.Request) {
log.Printf("[logger] %s", r.URL.String())
handler(w, r)
}
}
func RequestMiddleware(handler middleware.Handler) middleware.Handler {
return func(w http.ResponseWriter, r *http.Request) {
log.Printf("[request] %s", r.URL.String())
handler(w, r)
}
}
func PingHandle(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ping"))
}
func HelloHandle(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello"))
}
func main() {
m := middleware.NewMultiMiddleware()
m.Use(LoggerMiddleware)
m.Use(RequestMiddleware)
http.HandleFunc("/ping", m.Middleware(PingHandle))
http.HandleFunc("/hello", m.Middleware(HelloHandle))
http.ListenAndServe(":8080", nil)
}
选项模式
选项模式属于创建型模式的一种,适用于复杂对象的初始化,当对象初始化时大部分成员变量都使用默认值,只想对某一些成员赋值时,就可以使用选项模式
go
type Config struct {
Timeout time.Duration
Retry bool
}
type Option func(config *Config)
func WithTimeout(timeout time.Duration) Option {
return func(config *Config) {
config.Timeout = timeout
}
}
func WithRetry() Option {
return func(config *Config) {
config.Retry = true
}
}
type Client struct {
config *Config
}
func NewClient(opt ...Option) *Client {
config := &Config{
Timeout: time.Second,
Retry: false,
}
for _, option := range opt {
option(config)
}
c := &Client{config: config}
return c
}
func main() {
c := NewClient(WithTimeout(time.Hour), WithRetry())
fmt.Println(c.config) // 结果 &{1h0m0s true}
}
函数转接口对象(Function to interface object)
实现函数向接口对象的转换,这样使用者用起来就会非常灵活,在net.http包也用了这个模式(HandlerFunc),下面这个例子是把Redis函数转换成CacheInterface接口的对象
go
type CacheInterface interface {
Check(a string) error
}
func NewCache(cache CacheInterface) {
fmt.Println(cache.Check("abc"))
}
type RevertFunc func(a string) error
func (r RevertFunc) Check(a string) error {
return r(a)
}
func Revert2Cache(a func(a string) error) CacheInterface {
return RevertFunc(a)
}
func Redis(a string) error {
return nil
}
func main() {
NewCache(Revert2Cache(Redis))
}