From aa35d086f8c76fdc2246f9e7109bf64d86cc5707 Mon Sep 17 00:00:00 2001 From: yumaojun03 <719118794@qq.com> Date: Sun, 8 Feb 2026 16:44:15 +0800 Subject: [PATCH] =?UTF-8?q?```=20feat(interface):=20=E6=B7=BB=E5=8A=A0Go?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E6=8E=A5=E5=8F=A3=E6=95=99=E7=A8=8B=E5=92=8C?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加了完整的Go语言接口教学内容,包括: - README.md中详细介绍了Go语言接口的概念和特性 - 包含解耦、多态、扩展性等接口优势说明 - 提供面向接口编程的完整示例代码 - 展示了标准库中Reader、Writer等常用接口 - 实现了一个支持Reader和Writer接口的Buffer示例 - 在main.go中演示了接口的实际应用和类型断言用法 ``` --- day05/interface/README.md | 314 +++++++++++++++++++++++++++++++++++++- day05/interface/main.go | 199 ++++++++++++++++++++++++ 2 files changed, 512 insertions(+), 1 deletion(-) create mode 100644 day05/interface/main.go diff --git a/day05/interface/README.md b/day05/interface/README.md index 60d9408..79e0330 100644 --- a/day05/interface/README.md +++ b/day05/interface/README.md @@ -1 +1,313 @@ -# Go语言接口 \ No newline at end of file +# Go语言接口 + +[课件](https://gitee.com/infraboard/go-course/blob/master/zh-cn/base/interface.md) + ++ 解耦(Decoupling):调用者不需要知道具体类型,只需要知道接口 ++ 多态(Polymorphism):不同类型可以有相同的接口,统一处理 ++ 扩展性(Extensibility):新增类型时,不需要修改已有代码 + + +## 面向接口 + +```go +package main + +import "fmt" + +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} + // 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()) + // } + // //... + // } + + // 管理员,组织这些演技者,进行演讲 + // interface 先制定规范, 底层系统按照规范进行实现,上层业务代码的编写将会是透明的,简单高效 + // 这里的Speaker 和 Dog Cat Cow 都是type 他们是一种东西吗? + // speakers = []Dog{}{dog1,dog2, dog3} + // Speaker 是一种约束, 约束放入这个slice里面的对象,必须实现这个接口的所有方法 + speakers := []Speaker{dog, cat, cow} + for _, speaker := range speakers { + fmt.Println(speaker.Speak()) + } + // 只关注 方法(接口), 不关注 对象的编程的方式,就是面向接口 +} + +// 任何一个结构体 实现了这个接口的所有方法, 就实现了这个接口 +// 接口是一层抽象, 只要实现了这个接口的所有方法, 就可以被当做这个接口来使用,屏蔽了底层系统 +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 +} +``` + ++ 解耦: 调用者不需要知道具体类型,只需要知道接口, fmt.Println(speaker.Speak()) ++ 多态(Polymorphism):不同类型可以有相同的接口,统一处理, []Speaker{dog, cat, cow} ++ 扩展性(Extensibility):新增类型时,不需要修改已有代码 ++ Go语言的接口实现是隐式的, 底层系统按照规范进行实现,上层业务代码的编写将会是透明的,简单高效 + + +## 面向接口 + +```go +type Reader interface { + Read(p []byte) (n int, err error) +} + +// Writer is the interface that wraps the basic Write method. +// +// Write writes len(p) bytes from p to the underlying data stream. +// It returns the number of bytes written from p (0 <= n <= len(p)) +// and any error encountered that caused the write to stop early. +// Write must return a non-nil error if it returns n < len(p). +// Write must not modify the slice data, even temporarily. +// +// Implementations must not retain p. +type Writer interface { + Write(p []byte) (n int, err error) +} + +// Closer is the interface that wraps the basic Close method. +// +// The behavior of Close after the first call is undefined. +// Specific implementations may document their own behavior. +type Closer interface { + Close() error +} + +// Seeker is the interface that wraps the basic Seek method. +// +// Seek sets the offset for the next Read or Write to offset, +// interpreted according to whence: +// [SeekStart] means relative to the start of the file, +// [SeekCurrent] means relative to the current offset, and +// [SeekEnd] means relative to the end +// (for example, offset = -2 specifies the penultimate byte of the file). +// Seek returns the new offset relative to the start of the +// file or an error, if any. +// +// Seeking to an offset before the start of the file is an error. +// Seeking to any positive offset may be allowed, but if the new offset exceeds +// the size of the underlying object the behavior of subsequent I/O operations +// is implementation-dependent. +type Seeker interface { + Seek(offset int64, whence int) (int64, error) +} + +// ReadWriter is the interface that groups the basic Read and Write methods. +type ReadWriter interface { + Reader + Writer +} + +// ReadCloser is the interface that groups the basic Read and Close methods. +type ReadCloser interface { + Reader + Closer +} + +// WriteCloser is the interface that groups the basic Write and Close methods. +type WriteCloser interface { + Writer + Closer +} + +// ReadWriteCloser is the interface that groups the basic Read, Write and Close methods. +type ReadWriteCloser interface { + Reader + Writer + Closer +} + +// ReadSeeker is the interface that groups the basic Read and Seek methods. +type ReadSeeker interface { + Reader + Seeker +} + +// ReadSeekCloser is the interface that groups the basic Read, Seek and Close +// methods. +type ReadSeekCloser interface { + Reader + Seeker + Closer +} + +// WriteSeeker is the interface that groups the basic Write and Seek methods. +type WriteSeeker interface { + Writer + Seeker +} + +// ReadWriteSeeker is the interface that groups the basic Read, Write and Seek methods. +type ReadWriteSeeker interface { + Reader + Writer + Seeker +} +``` + +Io Reader和Writer接口 +```go +// Reader is the interface that wraps the basic Read method. +// +// Read reads up to len(p) bytes into p. It returns the number of bytes +// read (0 <= n <= len(p)) and any error encountered. Even if Read +// returns n < len(p), it may use all of p as scratch space during the call. +// If some data is available but not len(p) bytes, Read conventionally +// returns what is available instead of waiting for more. +// +// When Read encounters an error or end-of-file condition after +// successfully reading n > 0 bytes, it returns the number of +// bytes read. It may return the (non-nil) error from the same call +// or return the error (and n == 0) from a subsequent call. +// An instance of this general case is that a Reader returning +// a non-zero number of bytes at the end of the input stream may +// return either err == EOF or err == nil. The next Read should +// return 0, EOF. +// +// Callers should always process the n > 0 bytes returned before +// considering the error err. Doing so correctly handles I/O errors +// that happen after reading some bytes and also both of the +// allowed EOF behaviors. +// +// If len(p) == 0, Read should always return n == 0. It may return a +// non-nil error if some error condition is known, such as EOF. +// +// Implementations of Read are discouraged from returning a +// zero byte count with a nil error, except when len(p) == 0. +// Callers should treat a return of 0 and nil as indicating that +// nothing happened; in particular it does not indicate EOF. +// +// Implementations must not retain p. +type Reader interface { + Read(p []byte) (n int, err error) +} + +// Writer is the interface that wraps the basic Write method. +// +// Write writes len(p) bytes from p to the underlying data stream. +// It returns the number of bytes written from p (0 <= n <= len(p)) +// and any error encountered that caused the write to stop early. +// Write must return a non-nil error if it returns n < len(p). +// Write must not modify the slice data, even temporarily. +// +// Implementations must not retain p. +type Writer interface { + Write(p []byte) (n int, err error) +} +``` + +如何实现一个buf +```go +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 +} +``` \ No newline at end of file diff --git a/day05/interface/main.go b/day05/interface/main.go new file mode 100644 index 0000000..30da70e --- /dev/null +++ b/day05/interface/main.go @@ -0,0 +1,199 @@ +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