name: go-interfaces user-invocable: false description: 当涉及Go接口包括接口设计、鸭子类型和组合模式时使用。当设计Go API和抽象时使用。 allowed-tools:
- Bash
- Read
Go接口
掌握Go的接口系统,通过隐式实现和组合模式创建灵活、解耦的代码。
基本接口
定义和实现接口:
package main
import "fmt"
// 定义接口
type Writer interface {
Write(p []byte) (n int, err error)
}
// 实现接口(隐式)
type ConsoleWriter struct{}
func (cw ConsoleWriter) Write(p []byte) (n int, err error) {
fmt.Print(string(p))
return len(p), nil
}
func main() {
var w Writer = ConsoleWriter{}
w.Write([]byte("Hello, World!
"))
}
接口中的多个方法:
type Reader interface {
Read(p []byte) (n int, err error)
}
type ReadWriter interface {
Read(p []byte) (n int, err error)
Write(p []byte) (n int, err error)
}
// 实现ReadWriter
type File struct {
name string
}
func (f *File) Read(p []byte) (n int, err error) {
// 实现
return 0, nil
}
func (f *File) Write(p []byte) (n int, err error) {
// 实现
return len(p), nil
}
空接口
使用interface{}(Go 1.18+中的any):
// 接受任何类型
func printValue(v interface{}) {
fmt.Println(v)
}
// 现代语法(Go 1.18+)
func printAny(v any) {
fmt.Println(v)
}
func main() {
printValue(42)
printValue("hello")
printValue(true)
printAny(3.14)
}
类型断言:
func processValue(v interface{}) {
// 类型断言
if str, ok := v.(string); ok {
fmt.Println("字符串:", str)
}
// 类型切换
switch val := v.(type) {
case int:
fmt.Println("整数:", val)
case string:
fmt.Println("字符串:", val)
case bool:
fmt.Println("布尔值:", val)
default:
fmt.Println("未知类型")
}
}
接口组合
嵌入接口:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
// 组合接口
type ReadWriter interface {
Reader
Writer
}
type ReadWriteCloser interface {
Reader
Writer
Closer
}
// 标准库示例
import "io"
func useReadWriteCloser(rwc io.ReadWriteCloser) {
// 可以调用Read、Write和Close
rwc.Write([]byte("data"))
rwc.Close()
}
常见接口
标准库接口:
// Stringer接口
type Stringer interface {
String() string
}
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s (%d岁)", p.Name, p.Age)
}
// error接口
type error interface {
Error() string
}
type MyError struct {
Message string
}
func (e MyError) Error() string {
return e.Message
}
// sort.Interface
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
接口设计模式
小接口:
// 好:小、专注的接口
type Getter interface {
Get(key string) (value string, exists bool)
}
type Setter interface {
Set(key, value string)
}
type Deleter interface {
Delete(key string)
}
// 按需组合
type Cache interface {
Getter
Setter
Deleter
}
接受接口,返回结构体:
// 接受接口参数
func processReader(r io.Reader) error {
data, err := io.ReadAll(r)
if err != nil {
return err
}
fmt.Println(string(data))
return nil
}
// 返回具体类型
func newConfig() *Config {
return &Config{
Host: "localhost",
Port: 8080,
}
}
type Config struct {
Host string
Port int
}
Nil接口
理解nil接口:
func checkNil() {
var i interface{}
fmt.Println(i == nil) // true
var p *Person
i = p
fmt.Println(i == nil) // false!(类型已设置,值为nil)
// 正确的nil检查
v, ok := i.(*Person)
fmt.Println(v == nil, ok) // true, true
}
接口满足
检查接口实现:
// 编译时检查
var _ io.Writer = (*MyWriter)(nil)
var _ io.Reader = (*MyReader)(nil)
type MyWriter struct{}
func (w *MyWriter) Write(p []byte) (n int, err error) {
return len(p), nil
}
// 如果MyWriter未实现Writer,编译失败
鸭子类型
隐式接口满足:
// 无需显式"implements"关键字
type Duck interface {
Quack()
Walk()
}
type RealDuck struct{}
func (d RealDuck) Quack() {
fmt.Println("嘎嘎!")
}
func (d RealDuck) Walk() {
fmt.Println("摇摇摆摆")
}
type Robot struct{}
func (r Robot) Quack() {
fmt.Println("哔哔嘎嘎")
}
func (r Robot) Walk() {
fmt.Println("*机械行走声*")
}
func makeDuckDoThings(d Duck) {
d.Quack()
d.Walk()
}
func main() {
makeDuckDoThings(RealDuck{})
makeDuckDoThings(Robot{})
}
多态性
使用接口实现多态性:
type Shape interface {
Area() float64
Perimeter() float64
}
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return 3.14159 * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * 3.14159 * c.Radius
}
func printShapeInfo(s Shape) {
fmt.Printf("面积: %.2f, 周长: %.2f
",
s.Area(), s.Perimeter())
}
func main() {
shapes := []Shape{
Rectangle{Width: 10, Height: 5},
Circle{Radius: 7},
}
for _, shape := range shapes {
printShapeInfo(shape)
}
}
依赖注入
使用接口提高可测试性:
// 为依赖定义接口
type UserRepository interface {
GetUser(id int) (*User, error)
SaveUser(user *User) error
}
// 生产环境实现
type PostgresUserRepo struct {
db *sql.DB
}
func (r *PostgresUserRepo) GetUser(id int) (*User, error) {
// 数据库查询
return &User{}, nil
}
func (r *PostgresUserRepo) SaveUser(user *User) error {
// 数据库插入/更新
return nil
}
// 测试实现
type MockUserRepo struct {
users map[int]*User
}
func (m *MockUserRepo) GetUser(id int) (*User, error) {
user, exists := m.users[id]
if !exists {
return nil, errors.New("用户未找到")
}
return user, nil
}
func (m *MockUserRepo) SaveUser(user *User) error {
m.users[user.ID] = user
return nil
}
// 服务依赖接口,而非具体类型
type UserService struct {
repo UserRepository
}
func (s *UserService) GetUserName(id int) (string, error) {
user, err := s.repo.GetUser(id)
if err != nil {
return "", err
}
return user.Name, nil
}
type User struct {
ID int
Name string
}
建造者模式与接口
流式接口模式:
type QueryBuilder interface {
Select(fields ...string) QueryBuilder
From(table string) QueryBuilder
Where(condition string) QueryBuilder
Build() string
}
type sqlQueryBuilder struct {
selectFields []string
fromTable string
whereClause string
}
func NewQueryBuilder() QueryBuilder {
return &sqlQueryBuilder{}
}
func (b *sqlQueryBuilder) Select(fields ...string) QueryBuilder {
b.selectFields = fields
return b
}
func (b *sqlQueryBuilder) From(table string) QueryBuilder {
b.fromTable = table
return b
}
func (b *sqlQueryBuilder) Where(condition string) QueryBuilder {
b.whereClause = condition
return b
}
func (b *sqlQueryBuilder) Build() string {
query := "SELECT " + strings.Join(b.selectFields, ", ")
query += " FROM " + b.fromTable
if b.whereClause != "" {
query += " WHERE " + b.whereClause
}
return query
}
func main() {
query := NewQueryBuilder().
Select("id", "name", "email").
From("users").
Where("age > 18").
Build()
fmt.Println(query)
}
策略模式
实现策略模式:
type PaymentStrategy interface {
Pay(amount float64) error
}
type CreditCardPayment struct {
CardNumber string
}
func (c *CreditCardPayment) Pay(amount float64) error {
fmt.Printf("使用信用卡 %s 支付 %.2f
",
amount, c.CardNumber)
return nil
}
type PayPalPayment struct {
Email string
}
func (p *PayPalPayment) Pay(amount float64) error {
fmt.Printf("通过PayPal向 %s 支付 %.2f
",
amount, p.Email)
return nil
}
type ShoppingCart struct {
paymentMethod PaymentStrategy
}
func (cart *ShoppingCart) SetPaymentMethod(pm PaymentStrategy) {
cart.paymentMethod = pm
}
func (cart *ShoppingCart) Checkout(amount float64) error {
return cart.paymentMethod.Pay(amount)
}
func main() {
cart := &ShoppingCart{}
cart.SetPaymentMethod(&CreditCardPayment{CardNumber: "1234-5678"})
cart.Checkout(100.00)
cart.SetPaymentMethod(&PayPalPayment{Email: "user@example.com"})
cart.Checkout(50.00)
}
适配器模式
适配接口:
// 第三方日志器
type ThirdPartyLogger struct{}
func (t *ThirdPartyLogger) LogMessage(msg string, level int) {
fmt.Printf("[级别 %d] %s
", level, msg)
}
// 我们的应用接口
type Logger interface {
Info(msg string)
Error(msg string)
}
// 适配器
type LoggerAdapter struct {
thirdParty *ThirdPartyLogger
}
func (a *LoggerAdapter) Info(msg string) {
a.thirdParty.LogMessage(msg, 0)
}
func (a *LoggerAdapter) Error(msg string) {
a.thirdParty.LogMessage(msg, 2)
}
func useLogger(logger Logger) {
logger.Info("应用启动")
logger.Error("发生错误")
}
func main() {
adapter := &LoggerAdapter{
thirdParty: &ThirdPartyLogger{},
}
useLogger(adapter)
}
何时使用此技能
使用go-interfaces当您需要:
- 定义行为契约而无实现
- 启用多态性和代码重用
- 创建可测试代码与依赖注入
- 实现设计模式(策略、适配器等)
- 构建插件系统或可扩展架构
- 在大型应用中解耦组件
- 在测试中模拟依赖
- 在Go中遵循SOLID原则
- 创建灵活、可维护的API
- 支持相同行为的多种实现
最佳实践
- 保持接口小而专注(1-3个方法)
- 接受接口,返回具体类型
- 在接口使用处定义,而非实现处
- 使用接口组合处理复杂接口
- 除非绝对必要,不使用空接口
- 在编译时验证接口实现
- 在接口注释中记录预期行为
- 偏好多个小接口而非大接口
- 适用时使用标准库接口
- 以-er后缀命名接口(Reader、Writer等)
常见陷阱
- 使接口过大或过于通用
- 定义未使用的接口“以防万一”
- 返回接口而非具体类型
- 未正确处理nil接口值
- 对简单代码过度抽象
- 忘记接口是隐式满足的
- 过度使用空接口
- 未记录接口契约
- 为单一实现创建接口
- 混淆nil值与nil接口