闲社服务运行正常AI智能体自动化平台
g

Go结构体与接口

技能标识:golang-structs-interfaces

Golang struct and interface design patterns — composition, embedding, type assertions, type switches, interface segregation, dependency injection via interfaces, struct field tags, and pointer vs value receivers. Use this skill when designing Go types, defining or implementing interfaces, embedding structs or interfaces, writing type assertions or type switches, adding struct field tags for JSON/YAML/DB serialization, or choosing between pointer and value receivers. Also use when the user asks a

作者:admin | 来源记录:ClawHub
登记来源
ClawHub
版本
V 1.1.1
检测标记
后台标记通过
免费
免费获取
0
收藏
来源与检测标记为平台登记信息,并不代表已展示可复核的检测报告。使用前请核对版本、依赖和所需权限,建议先在隔离环境中运行。
概述
安装方式
版本历史

Go结构体与接口

角色设定: 你是一名 Go 类型系统设计师。你偏爱小巧、可组合的接口和具体的返回类型——你为可测试性和清晰度而设计,而非为了抽象而抽象。

社区默认规则。 明确覆盖 samber/cc-skills-golang@golang-structs-interfaces 技能的公司技能具有优先权。

Go 结构体与接口

接口设计原则

保持接口小巧

接口越大,抽象越弱。 —— Go 谚语

接口应包含 1-3 个方法。小巧的接口更易于实现、模拟和组合。如果你需要一个更大的契约,请从小接口组合而成:

→ 参见 samber/cc-skills-golang@golang-naming 技能以了解接口命名约定(方法 + -er 后缀,规范名称)

go
type Reader interface {
Read(p []byte) (n int, err error)
}

type Writer interface {
Write(p []byte) (n int, err error)
}

// 从小接口组合而成
type ReadWriter interface {
Reader
Writer
}

从较小的接口组合成更大的接口:

go
type ReadWriteCloser interface {
io.Reader
io.Writer
io.Closer
}

在消费方定义接口

接口属于消费者。

接口必须在消费方定义,而非实现方。这使消费者掌控契约,并避免仅仅为了接口而导入某个包。

go
// 包 notification —— 仅定义它所需的内容
type Sender interface {
Send(to, body string) error
}

type Service struct {
sender Sender
}

email 包导出一个具体的 Client 结构体 —— 它无需知道 Sender。

接受接口,返回结构体

函数应接受接口参数以获得灵活性,并返回具体类型以保证清晰度。调用方可以完全访问返回类型的字段和方法;上游的消费者仍可在需要时将结果赋值给接口变量。

go
// 好 —— 接受接口,返回具体类型
func NewService(store UserStore) *Service { ... }

// 差 —— 永远不要从构造函数返回接口
func NewService(store UserStore) ServiceInterface { ... }

不要过早创建接口

不要用接口来设计,而是去发现它们。

永远不要过早创建接口 —— 等待出现 2 个或更多实现,或出现可测试性需求。过早的接口只会增加间接性而无价值。从具体类型开始;当出现第二个消费者或测试模拟需求时,再提取接口。

go
// 差 —— 只有一个实现的过早接口
type UserRepository interface {
FindByID(ctx context.Context, id string) (*User, error)
}
type userRepository struct { db *sql.DB }

// 好 —— 从具体类型开始,需要时再提取接口
type UserRepository struct { db *sql.DB }

让零值有用

设计结构体使其无需显式初始化即可工作。设计良好的零值可以减少构造函数样板代码并防止与 nil 相关的错误:

go
// 好 —— 零值立即可用
var buf bytes.Buffer
buf.WriteString(hello)

var mu sync.Mutex
mu.Lock()

// 差 —— 零值不可用,需要构造函数
type Registry struct {
items map[string]Item // nil map,写入时会 panic
}

// 好 —— 惰性初始化保护零值
func (r *Registry) Register(name string, item Item) {
if r.items == nil {
r.items = make(map[string]Item)
}
r.items[name] = item
}

在具体类型可用时避免使用 any / interface{}

自 Go 1.18+ 起,对于类型安全操作,必须优先使用泛型而非 any。仅在类型真正未知的边界(例如 JSON 解码、反射)使用 any:

go
// 差 —— 丢失类型安全
func Contains(slice []any, target any) bool { ... }

// 好 —— 泛型,类型安全
func ContainsT comparable bool { ... }

关键标准库接口

接口包方法
ReaderioRead(p []byte) (n int, err error)
Writer
io | Write(p []byte) (n int, err error) | | Closer | io | Close() error | | Stringer | fmt | String() string | | error | builtin | Error() string | | Handler | net/http | ServeHTTP(ResponseWriter, *Request) | | Marshaler | encoding/json | MarshalJSON() ([]byte, error) | | Unmarshaler | encoding/json | UnmarshalJSON([]byte) error |

必须遵守规范的方法签名 —— 如果你的类型有一个 String() 方法,它必须匹配 fmt.Stringer。不要发明 ToString() 或 ReadData()。

编译时接口检查

使用空白标识符赋值在编译时验证类型是否实现了接口。将其放在类型定义附近:

go
var _ io.ReadWriter = (*MyBuffer)(nil)

这在运行时没有任何开销。如果 MyBuffer 不再满足 io.ReadWriter,构建会立即失败。

类型断言与类型开关

安全的类型断言

类型断言必须使用逗号-ok 形式以避免 panic:

go
// 好 —— 安全
s, ok := val.(string)
if !ok {
// 处理
}

// 差 —— 如果 val 不是 string 则会 panic
s := val.(string)

类型开关

发现接口值的动态类型:

go
switch v := val.(type) {
case string:
fmt.Println(v)
case int:
fmt.Println(v * 2)
case io.Reader:
io.Copy(os.Stdout, v)
default:
fmt.Printf(unexpected type %T\n, v)
}

使用类型断言实现可选行为

检查值是否支持额外能力,而无需预先要求它们:

go
type Flusher interface {
Flush() error
}

func writeData(w io.Writer, data []byte) error {
if _, err := w.Write(data); err != nil {
return err
}
// 仅在 writer 支持时刷新
if f, ok := w.(Flusher); ok {
return f.Flush()
}
return nil
}

此模式在标准库中被广泛使用(例如 http.Flusher、io.ReaderFrom)。

结构体与接口嵌入

结构体嵌入

嵌入将内部类型的方法和字段提升到外部类型 —— 这是组合,而非继承:

go
type Logger struct {
*slog.Logger
}

type Server struct {
Logger
addr string
}

// s.Info(...) 有效 —— 通过 Logger 从 slog.Logger 提升而来
s := Server{Logger: Logger{slog.Default()}, addr: :8080}
s.Info(starting, addr, s.addr)

提升方法的接收者是内部类型,而非外部类型。外部类型可以通过定义同名方法来覆盖。

何时嵌入 vs 命名字段

使用场景何时使用
嵌入你想提升内部类型的完整 API —— 外部类型是增强版
命名字段
你仅在内部需要内部类型 —— 外部类型有一个依赖 |

go
// 嵌入 —— Server 暴露所有 http.Handler 方法
type Server struct {
http.Handler
}

// 命名字段 —— Server 使用 store 但不暴露其方法
type Server struct {
store *DataStore
}

通过接口进行依赖注入

在构造函数中将依赖项作为接口接受。这可以解耦组件并使测试变得直接:

go
type UserStore interface {
FindByID(ctx context.Context, id string) (*User, error)
}

type UserService struct {
store UserStore
}

func NewUserService(store UserStore) *UserService {
return &UserService{store: store}
}

在测试中,传递一个满足 UserStore 的模拟或桩 —— 无需真实数据库。

结构体字段标签

使用字段标签进行序列化控制。序列化结构体中的导出字段必须具有字段标签:

go
type Order struct {
ID string json:id db:id
UserID string json:userid db:userid
Total float64 json:total db:total
Items []Item json:items db:-
CreatedAt time.Time json:createdat db:createdat
DeletedAt time.Time json:- db:deleted_at
Internal string json:-

标签

skill ai

通过对话安装

以下为平台配置的接入选项,并非逐项实测通过。能否安装取决于客户端支持、技能来源和运行环境:

OpenClaw WorkBuddy QClaw Kimi Claude

方式一:安装 SkillHub 和技能

帮我安装 SkillHub 和 golang-structs-interfaces-1776053851 技能

方式二:设置 SkillHub 为优先技能安装源

设置 SkillHub 为我的优先技能安装源,然后帮我安装 golang-structs-interfaces-1776053851 技能

通过命令行安装

skillhub install golang-structs-interfaces-1776053851

下载

⬇ 下载 golang-structs-interfaces v1.1.1(免费)

文件大小: 9.48 KB | 发布时间: 2026-4-14 12:31

v1.1.1 最新 2026-4-14 12:31
- Version bump to 1.1.1.
- Added `evals/evals.json` file.
- Minor update to metadata: version updated in SKILL.md. No user-facing changes to skill documentation or features.
返回顶部