接口/结构体的组合
接口
接口 = 定义方法集
1
2
3
4
5
6
7
type FImpl interface {
f1()
f2()
f3()
f4()
}
实现
结构体实现遵循规则:
结构体T方法为值接收者方法: T 和 *T 都可以赋给接口
- 结构体T方法为接收者方法: 只有 *T 可以赋给接口
- 接口类型前面永远不要加 * (接口用值,不加星;加星就废,没方法)
示例1
结构体T方法为值接收者方法: T 和 *T 都可以赋给接口
结构体T方法为接收者方法:只有 *T 可以赋给接口
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
type F interface {
f()
}
type S1 struct{}
func (s S1) f() {} // 值接收者方法,属于 S1 的值方法集
type S2 struct{}
func (s *S2) f() {} // 指针接收者方法,属于 *S2 的指针方法集
var (
i F // 接口
//值方法集
s1Val = S1{}
s1Ptr = &S1{}
i = s1Val
i = s1Ptr
//指针方法集
s2Val = S2{}
s2Ptr = &S2{}
i = s2Ptr //只能将指针对象赋值给接口变量
)
示例2
接口类型前面永远不要加 * (接口用值,不加星;加星就废,没方法)
1
2
3
4
5
6
7
8
9
10
11
//httpclient包
type Store interface {
Get(key string) (string, error)
Set(key, val string) error
}
var _defaultInstance *httpclient.Store // 错
func GetInstance() *httpclient.Store // 错 //GetInstance().Get() 报错找不到这个方法
var _defaultInstance httpclient.Store // 对
func GetInstance() httpclient.Store // 对
多态
- 入参为接口 → 调用端可传任意实现者
- 返回为接口 → 调用端只能按自定义规则返回任意实现者
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
package main
import "fmt"
// 1. 定义两个接口
type Speaker interface {
Talk() string
}
type Singer interface {
Speaker // 嵌套,会 Talk 已足够
Sing() string
}
// 2. 两个具体类型
type Dog struct{}
type Person struct{ name string }
// Dog 只实现 Speaker
func (Dog) Talk() string { return "woof" }
// Person 同时实现 Speaker 和 Singer
func (Person) Talk() string { return "hello" }
func (Person) Sing() string { return "lalala" }
// 3. [入参为接口:形参写成接口]
func greet(s Speaker) {
fmt.Println("greet:", s.Talk())
}
// 4. [返回为接口:调用端只能按接口方法集用]
func factory(kind string) Speaker {
switch kind {
case "dog":
return Dog{}
case "person":
return Person{name: "Alice"}
}
return nil
}
func main() {
// ① 多态传参:任何实现了 Speaker 的都能塞进来
greet(Dog{}) // 输出:greet: woof
greet(Person{name: "Bob"}) // 输出:greet: hello
// ② 返回的是 Speaker 接口,只能调 Talk()
sp := factory("person")
fmt.Println("factory talk:", sp.Talk()) // 合法
// sp.Sing() // 编译错误:Speaker 没有 Sing 方法
}
接口套接口
接口套接口 = 方法集的并集
定义接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ==================== 第一步:定义小接口(单一职责)====================
// Reader 接口:只负责读
type Reader interface {
Read(p []byte) (int, error)
}
// Writer 接口:只负责写
type Writer interface {
Write(p []byte) (int, error)
}
// ==================== 第二步:接口套接口 - 组合成大接口 ====================
// ReadWriter 接口:组合了 Reader 和 Writer 的能力
type ReadWriter interface {
Reader // 嵌入 Reader 接口(接口套接口)
Writer // 嵌入 Writer 接口(接口套接口)
}
实现接口
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
// ==================== 第三步:实现接口 ====================
// 实现 1:只读实现(只实现 Reader)
type ReadOnlyBuffer struct {
data []byte
pos int
}
// 构造:创建只读缓冲区
func NewReadOnlyBuffer(data []byte) *ReadOnlyBuffer {
return &ReadOnlyBuffer{
data: data,
pos: 0,
}
}
// 实现 Reader 接口
func (r *ReadOnlyBuffer) Read(p []byte) (int, error) {
if r.pos >= len(r.data) {
return 0, errors.New("EOF")
}
n := copy(p, r.data[r.pos:])
r.pos += n
return n, nil
}
// 实现 2:只写实现(只实现 Writer)
type WriteOnlyBuffer struct {
data []byte
}
// 构造:创建只写缓冲区
func NewWriteOnlyBuffer() *WriteOnlyBuffer {
return &WriteOnlyBuffer{
data: make([]byte, 0),
}
}
// 实现 Writer 接口
func (w *WriteOnlyBuffer) Write(p []byte) (int, error) {
w.data = append(w.data, p...)
return len(p), nil
}
// 获取写入的数据
func (w *WriteOnlyBuffer) GetData() []byte {
return w.data
}
// 实现 3:读写实现(同时实现 Reader 和 Writer,自动满足 ReadWriter)
type ReadWriteBuffer struct {
data []byte
pos int
}
// 构造:创建读写缓冲区
func NewReadWriteBuffer(initialData []byte) *ReadWriteBuffer {
return &ReadWriteBuffer{
data: initialData,
pos: 0,
}
}
// 实现 Reader 接口
func (rw *ReadWriteBuffer) Read(p []byte) (int, error) {
if rw.pos >= len(rw.data) {
return 0, errors.New("EOF")
}
n := copy(p, rw.data[rw.pos:])
rw.pos += n
return n, nil
}
// 实现 Writer 接口
func (rw *ReadWriteBuffer) Write(p []byte) (int, error) {
rw.data = append(rw.data, p...)
return len(p), nil
}
// 获取所有数据
func (rw *ReadWriteBuffer) GetData() []byte {
return rw.data
}
// 重置读取位置
func (rw *ReadWriteBuffer) Reset() {
rw.pos = 0
}
使用
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// ==================== 第四步:使用接口的函数(调用方)====================
// 函数 1:只需要读能力(依赖小接口 Reader)
func ReadData(r Reader, buf []byte) (int, error) {
fmt.Println("调用 ReadData,只需要 Reader 接口")
return r.Read(buf)
}
// 函数 2:只需要写能力(依赖小接口 Writer)
func WriteData(w Writer, data []byte) (int, error) {
fmt.Println("调用 WriteData,只需要 Writer 接口")
return w.Write(data)
}
// 函数 3:需要读写能力(依赖组合接口 ReadWriter)
func ReadAndWrite(rw ReadWriter, readBuf []byte, writeData []byte) error {
fmt.Println("调用 ReadAndWrite,需要 ReadWriter 接口(Reader + Writer)")
// 可以调用 Reader 的方法
n, err := rw.Read(readBuf)
if err != nil {
return err
}
fmt.Printf(" 读取了 %d 字节\n", n)
// 可以调用 Writer 的方法
n, err = rw.Write(writeData)
if err != nil {
return err
}
fmt.Printf(" 写入了 %d 字节\n", n)
return nil
}
// ==================== 第五步:完整的使用示例 ====================
// ExampleUsage 展示完整的使用流程
func ExampleUsage() {
fmt.Println("========== 接口套接口示例:构造 + 调用 ==========\n")
// ========== 场景 1:只读场景 ==========
fmt.Println("【场景 1】只读场景:使用 ReadOnlyBuffer(只实现 Reader)")
readOnly := NewReadOnlyBuffer([]byte("Hello, World!"))
// 构造:readOnly 是 *ReadOnlyBuffer 类型
// 调用:可以当作 Reader 接口使用
buf1 := make([]byte, 5)
n1, err1 := ReadData(readOnly, buf1)
if err1 == nil {
fmt.Printf(" 读取结果: %s (读取了 %d 字节)\n\n", string(buf1[:n1]), n1)
}
// ========== 场景 2:只写场景 ==========
fmt.Println("【场景 2】只写场景:使用 WriteOnlyBuffer(只实现 Writer)")
writeOnly := NewWriteOnlyBuffer()
// 构造:writeOnly 是 *WriteOnlyBuffer 类型
// 调用:可以当作 Writer 接口使用
_, err2 := WriteData(writeOnly, []byte("Hello"))
if err2 == nil {
fmt.Printf(" 写入成功,缓冲区数据: %s\n\n", string(writeOnly.GetData()))
}
// ========== 场景 3:读写场景 ==========
fmt.Println("【场景 3】读写场景:使用 ReadWriteBuffer(实现 ReadWriter)")
rwBuffer := NewReadWriteBuffer([]byte("Initial Data"))
// 构造:rwBuffer 是 *ReadWriteBuffer 类型
// 调用:可以当作 ReadWriter 接口使用(因为它同时实现了 Reader 和 Writer)
readBuf := make([]byte, 10)
err3 := ReadAndWrite(rwBuffer, readBuf, []byte(" + New Data"))
if err3 == nil {
fmt.Printf(" 最终数据: %s\n\n", string(rwBuffer.GetData()))
}
// ========== 场景 4:多态使用 ==========
fmt.Println("【场景 4】多态使用:同一个函数可以接受不同的实现")
// 可以传入只读实现
fmt.Println(" - 传入 ReadOnlyBuffer(只读):")
ReadData(NewReadOnlyBuffer([]byte("Read only")), make([]byte, 5))
// 可以传入读写实现(因为 ReadWriteBuffer 也实现了 Reader)
fmt.Println(" - 传入 ReadWriteBuffer(读写,但这里只当 Reader 用):")
ReadData(NewReadWriteBuffer([]byte("Read from RW")), make([]byte, 5))
// 可以传入读写实现(当作 ReadWriter 用)
fmt.Println(" - 传入 ReadWriteBuffer(当作 ReadWriter 用):")
ReadAndWrite(NewReadWriteBuffer([]byte("Start")), make([]byte, 5), []byte("End"))
fmt.Println("\n========== 示例结束 ==========")
}
结构体里套接口
命名字段方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 接口
type Storage interface {
Save(key string, val []byte) error
Load(key string) ([]byte, error)
}
// 结构体
type Session struct {
store Storage // 结构体里面“套”了一个接口 //使用接口的任意实现者
}
// 结构体构造函数 -> 注入实现, 可传任意实现者
func NewSession(s Storage) *Session {
return &Session{store: s}
}
// 调用:通过接口字段使用
func (s *Session) Set(k string, v []byte) error {
return s.store.Save(k, v) // 只知道是 Storage 接口
}
使用示例:
1
2
3
4
5
6
7
8
9
10
// 构造时传入不同实现
redisStore := &RedisStorage{}
session := NewSession(redisStore)
// 构造时传入不同实现
memoryStore := &MemoryStorage{}
session := NewSession(memoryStore)
// 调用时都一样
session.Set("key", []byte("value"))
匿名嵌入接口(不建议)
1
2
3
4
5
6
7
8
9
10
11
type Closer interface {
Close() error
}
type Conn struct {
Closer // 匿名嵌入接口
}
// 可以直接调用
conn.Close() // 如果 Conn 实现了 Closer,会调用 Conn 的方法
// 如果 Conn 没实现,会调用嵌入的 Closer 字段的方法
结构体套结构体
写法
这里主要包括两种情况:
- 字段形式的嵌套:
type A struct { B BType } - 匿名嵌入:
type A struct { BType }
写法1:
1
2
3
4
5
6
type Read struct { /* ... */ }
type User struct {
ID int64
Read Read
}
写法2:
1
2
3
4
5
6
type UserBase struct { /* ... */ }
type User struct {
ID int64
UserBase
}
聚合多个子组件(服务组合)
场景:一个“服务/管理器”整合很多子服务:
SessionManager里有Store,Logger,Notifier等。多见于:
Server、Application、Manager这种总控类。
特点:
- 强调一个对象中*组合多个子职责,对外作为一个整体入口。
示例
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
// 接口定义 type Store interface { Get(key string) ([]byte, error) Set(key string, val []byte) error } // 结构体套接口 type SessionManager struct { store Store // 存储接口 logger Logger // 日志接口(假设已定义) cache Cache // 缓存接口(假设已定义) } // 构造:注入所有依赖 func NewSessionManager(s Store, l Logger, c Cache) *SessionManager { return &SessionManager{ store: s, logger: l, cache: c, } } // 使用:通过接口字段调用 func (sm *SessionManager) GetSession(id string) (*Session, error) { // 使用传入的logger实现 sm.logger.Info("getting session", id) // 使用传入的cache实现 if val, err := sm.cache.Get(id); err == nil { return parseSession(val) } }
复用方法/字段(匿名组合)
- 场景:想让结构体“直接拥有另一个结构体的方法/字段”,达到类似“继承”的效果。
- 举例:
- 公共的
BaseModel、BaseSession,里面有ID,CreatedAt等。 - HTTP handler 里嵌入一个
Logger或Context方便直接调用。
- 公共的
特点:
- 外层结构体对外暴露内层结构体的方法:
outer.InnerMethod()这种“转发”自动完成。 - 用来实现“代码复用 + 行为扩展”(比继承更灵活)。
- 外层结构体对外暴露内层结构体的方法:
示例
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 55 56 57 58 59 60
package main import ( "fmt" "time" ) // 1. 公共“基类”:BaseModel 提供通用字段和方法 type BaseModel struct { ID int64 CreatedAt time.Time } func (b BaseModel) String() string { return fmt.Sprintf("ID=%d CreatedAt=%v", b.ID, b.CreatedAt) } // 2. 再给一个 Logger 组件,也能被任意结构体“混”进去 type Logger struct { Prefix string } func (l Logger) Log(msg string) { fmt.Printf("[%s] %s\n", l.Prefix, msg) } // 3. 业务结构体:把 BaseModel 和 Logger 匿名组合进来 // 现在 Order 就“自动拥有”了 ID/CreatedAt 字段 和 String()/Log() 方法 type Order struct { BaseModel // 匿名组合,字段和方法全部提升 Logger // 再来一个组件 Customer string } // 4. 外层还可以“覆盖”内层方法(同名即可) func (o Order) String() string { return fmt.Sprintf("Order<%s,%s>", o.Customer, o.BaseModel.String()) } func main() { // 5. 创建实例 o := Order{ BaseModel: BaseModel{ ID: 10086, CreatedAt: time.Now(), }, Logger: Logger{Prefix: "Order"}, Customer: "Alice", } // 6. 直接访问被提升的字段 fmt.Println("ID :", o.ID) // 来自 BaseModel fmt.Println("CreatedAt :", o.CreatedAt) // 来自 BaseModel // 7. 直接调用被提升的方法 o.Log("order created") // 来自 Logger // 8. 外层 String 已覆盖内层版本 fmt.Println("String() :", o.String()) // 来着 Order }
1 2 3 4
ID : 10086 CreatedAt : 2009-11-10 23:00:00 +0000 UTC m=+0.000000001 [Order] order created String() : Order<Alice,ID=10086 CreatedAt=2009-11-10 23:00:00 +0000 UTC m=+0.000000001>