go20/day05/interface/main.go
yumaojun03 aa35d086f8 ```
feat(interface): 添加Go语言接口教程和示例代码

添加了完整的Go语言接口教学内容,包括:

- README.md中详细介绍了Go语言接口的概念和特性
- 包含解耦、多态、扩展性等接口优势说明
- 提供面向接口编程的完整示例代码
- 展示了标准库中Reader、Writer等常用接口
- 实现了一个支持Reader和Writer接口的Buffer示例
- 在main.go中演示了接口的实际应用和类型断言用法
```
2026-02-08 16:44:15 +08:00

200 lines
4.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"fmt"
"io"
"os"
)
func main() {
//
dog := Dog{Name: "旺财"}
fmt.Println(dog.Speak())
//
cat := Cat{Name: "招财猫"}
fmt.Println(cat.Speak())
//
cow := Cow{Name: "小牛"}
fmt.Println(cow.Speak())
// 必须知道对象的对象, 才能调用, 面向具体类型来编写代码, 依赖具体类型, 耦合度高
// 没有一层统一抽象,上层业务逻辑,需要知道对象,才能调用,非常麻烦,高耦合
// speakers := []interface{}{dog, cat, cow}
// speakers := []any{dog, cat, cow}
// for _, speaker := range speakers {
// switch speaker.(type) {
// case Dog:
// fmt.Println(speaker.(Dog).Speak())
// case Cat:
// fmt.Println(speaker.(Cat).Speak())
// case Cow:
// fmt.Println(speaker.(Cow).Speak())
// case Person:
// fmt.Println(speaker.(Person).Speak())
// }
// //...
// }
person := Person{Name: "小明"}
// 管理员,组织这些演技者,进行演讲
// interface 先制定规范, 底层系统按照规范进行实现,上层业务代码的编写将会是透明的,简单高效
// 这里的Speaker 和 Dog Cat Cow 都是type 他们是一种东西吗?
// speakers = []Dog{}{dog1,dog2, dog3}
// Speaker 是一种约束, 约束放入这个slice里面的对象必须实现这个接口的所有方法
speakers := []Speaker{dog, cat, cow, person}
for _, speaker := range speakers {
fmt.Println(speaker.Speak())
// 类型断言, 判断这个speaker是否是Dog类型, 如果是就调用Dog的GetName方法
// 断言成一个具体的类型来使用,一般不建议,因为这样就失去了接口的意义,接口的意义就是不关心具体类型,只关心方法
// if dog, ok := speaker.(Dog); ok {
// fmt.Println(dog.GetName())
// }
// 不安全方式会panic
// dog := a.(Dog) // 如果a不是Dog类型会panic
// speaker.(Dog).GetName() // 只能调用接口的方法, 不能调用具体类型的方法
}
// 只关注 方法(接口), 不关注 对象的编程的方式,就是面向接口
// 1. 使用固定1k缓冲区循环读取文件并输出
buf := NewBuffer()
f, err := os.Open("main.go")
if err != nil {
panic(err)
}
defer f.Close()
// 循环读取:文件 -> 缓冲区 -> 标准输出
for {
// 清空缓冲区准备接收新数据
buf.Reset()
// 从文件读取最多1k数据到缓冲区
n, err := io.CopyN(buf, f, 1024)
if n > 0 {
// 将缓冲区的数据输出到标准输出
fmt.Printf("\n--- 读取了 %d 字节 ---\n", n)
io.Copy(os.Stdout, buf)
}
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
}
}
// 任何一个结构体 实现了这个接口的所有方法, 就实现了这个接口
// 接口是一层抽象, 只要实现了这个接口的所有方法, 就可以被当做这个接口来使用,屏蔽了底层系统
type Speaker interface {
Speak() string // 说话方法
GetName() string // 获取名字方法
}
// 2. 定义不同的类型
type Dog struct {
Name string
}
// 3. 实现接口方法
func (d Dog) Speak() string {
return "汪汪汪"
}
func (d Dog) GetName() string {
return d.Name
}
type Cat struct {
Name string
}
func (c Cat) Speak() string {
return "喵喵喵"
}
func (c Cat) GetName() string {
return c.Name
}
type Cow struct {
Name string
}
func (c Cow) Speak() string {
return "哞哞哞"
}
func (c Cow) GetName() string {
return c.Name
}
// 标注自己实现了Speaker 不需要
type Person struct {
Name string
}
func (p Person) Speak() string {
return "hello"
}
func (p Person) GetName() string {
return p.Name
}
func NewBuffer() *Buffer {
return &Buffer{
// 固定 1k 大小的缓冲区
buf: make([]byte, 1024),
dataLen: 0,
}
}
// 实现 Reader和 Writer方法
type Buffer struct {
buf []byte // 固定大小的缓冲区
dataLen int // 当前缓冲区中的数据量
}
func (b *Buffer) Read(p []byte) (n int, err error) {
if b.dataLen == 0 {
return 0, io.EOF
}
// 读取缓冲区中的数据
n = copy(p, b.buf[:b.dataLen])
fmt.Println("read: ", n)
// 移除已读数据(将未读数据前移)
copy(b.buf, b.buf[n:b.dataLen])
b.dataLen -= n
return n, nil
}
func (b *Buffer) Write(p []byte) (n int, err error) {
// 计算剩余空间
available := len(b.buf) - b.dataLen
if available == 0 {
// 缓冲区已满
return 0, io.ErrShortBuffer
}
// 只写入缓冲区能容纳的数据(固定大小,不扩展)
n = copy(b.buf[b.dataLen:], p)
b.dataLen += n
fmt.Printf("write: %d 字节, 缓冲区使用: %d/%d\n", n, b.dataLen, len(b.buf))
// 如果没能写入全部数据,返回 ErrShortBuffer
if n < len(p) {
return n, io.ErrShortBuffer
}
return n, nil
}
func (b *Buffer) String() string {
return string(b.buf[:b.dataLen])
}
// Reset 清空缓冲区
func (b *Buffer) Reset() {
b.dataLen = 0
}
// end