文章

接口/结构体的组合

接口

接口 = 定义方法集

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. 返回为接口 → 调用端只能按自定义规则返回任意实现者
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 字段的方法

结构体套结构体

写法

这里主要包括两种情况:

  1. 字段形式的嵌套:type A struct { B BType }
  2. 匿名嵌入: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 等。

  • 多见于:

    • ServerApplicationManager 这种总控类。
  • 特点:

    • 强调一个对象中*组合多个子职责,对外作为一个整体入口。
  • 示例

    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)
        }
    }
    

复用方法/字段(匿名组合)

  • 场景:想让结构体“直接拥有另一个结构体的方法/字段”,达到类似“继承”的效果。
  • 举例:
    • 公共的 BaseModelBaseSession,里面有 ID, CreatedAt 等。
    • HTTP handler 里嵌入一个 LoggerContext 方便直接调用。
  • 特点:

    • 外层结构体对外暴露内层结构体的方法: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>
    

© 2024- lfj