如果说C++和Java是关于类型层次结构和类型分类的语言,那么Go则是关于组合的语言。
——Rob Pike,Go语言之父

“偏好组合,正交解耦”是Go语言的重要设计哲学之一。本篇文章将重点介绍Go语言的设计哲学。

组合方式

从组合方式上看,Go语言中的组合方式主要有垂直组合和水平组合两种。
垂直组合(类型组合):Go语言主要通过类型嵌入机制实现垂直组合,进而实现方法实现的复用、接口定义重用等。
水平组合:通常Go程序以接口类型变量作为程序水平组合连接点。

垂直组合

通过嵌入接口构建接口:通过在接口定义中嵌入其他接口类型实现接口行为聚合,组成大接口。

1
2
3
4
type ReadWriter interface {
Reader
Writer
}

通过嵌入接口构建结构体:通过嵌入接口类型的方式构建结构体类型

1
2
3
4
type MyReader struct{
io.Reader
N int64
}

通过嵌入结构体构建新结构体:通过嵌入结构体类型的方式构建新的结构体类型

1
2
3
4
5
6
7
// $GOROOT/src/syc/pool.go
type poolLocal struct {
private interface{}
shared []interface{}
Mutex
pad [128]byte
}

水平组合

基本形式

水平组合的基本形式是接受接口类型参数的函数或方法,接口类型的参数作为连接点,将位于多个包中的多个类型的接口实现者“编织”到一起,共同形成一副程序“骨架”。同时,接口类型与其实现者之间隐式的关系在不经意间满足了依赖抽象、里氏替换原则、接口隔离等代码设计原则。

1
func FuncName(param YourInterfaceType)

标准库中的例子:

1
2
// $GOROOT/src/io/ioutil/ioutil.go
func readAll(r io.Reader, capacity int64) (b []byte, err error)

包裹函数

包裹函数的形式接受接口类型参数,并返回与其参数类型相同的返回值。
通过包裹函数可以实现对输入数据的过滤、装饰、变换等操作,并将结果再次返回给调用者。

1
func WrapperFunc(param YourInterfaceType) YourInterfaceType

标准库中的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//$GOROOT/src/io/io.go
func LimitReader(r Reader, n int64) Reader {
return &LimitedReader{r,n}
}

type LimitReader struct{
R Reader
N int64
}

func (l *LimitReader) Read(p []byte) (n int, err error){
...
}


//LimitReader函数输入参数是io.Reader接口类型,返回值的类型依然是io.Reader
//LimitReader类型包裹了io.Reader接口,并重写了io.Reader接口的Read方法,实现了对输入数据的过滤、装饰、变换等操作

适配器函数类型

配器函数类型是一个辅助水平组合实现的”工具”类型。它是一个类型,它可以将一个满足特定函数签名的普通函数显式转换成自身类型的实例,转换后的实例同时也是某个单方法接口类型的实现者。
著名的适配器函数类型http.HandlerFunc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}

//HandlerFunc是适配器函数类型,HandlerFunc类型的变量同时是满足Handler接口类型的实现者
type HandlerFunc func(ResponseWriter,*Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request){
f(w,r)
}

//用法例子
func greetings(w http.ResponseWriter, r *http.Request){
fmt.Fprintf(w, "Welcome")
}

func main() {
//http.HandlerFunc(greetings) 将greetings函数转换成了HandlerFunc类型,
//HandlerFunc类型的变量同时是满足Handler接口类型的实现者
http.ListenAndServe(":8080", http.HandlerFunc(greetings))
}

中间件

中间件包裹函数适配器函数类型结合的产物。常见于Web编程中:

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
package main

import (
"fmt"
"log"
"net/http"
"time"
)

func validateAuth(s string) error {
if s != "admin" {
return fmt.Errorf("invalid auth: %s", s)
}

return nil
}

func greetings(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Welcome!")
}

// 中间件
func logHandler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t := time.Now()
log.Printf("[%s] %q %v\n", r.Method, r.URL.String(), t)
h.ServeHTTP(w, r)
})
}

// 中间件
func authHandler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
err := validateAuth(r.Header.Get("auth"))
if err != nil {
http.Error(w, "bad auth param", http.StatusUnauthorized)
return
}
h.ServeHTTP(w, r)
})
}

func main() {

http.ListenAndServe(":8080", logHandler(authHandler(http.HandlerFunc(greetings))))
}