Go语言中常见100问题-#13 你在项目中创建utils包了吗?

使用utils包有啥问题?

本文将讨论Go语言中一种常见的不好的实践:创建utils、common和base等共享包。首先分析这种做法存在的问题,然后讨论如何改进。

下面是一个受Go官方博客启发构造的例子,实现一个集合数据结构。在Go语言中完成该功能的惯用方法是通过 mapKstruct{} 类型来处理,K是map中允许的任何类型作为键,而值是 struct{} 类型, 表示我们对值不关心。实现代码如下,在util包中提供了两个对外的函数。

golang 复制代码
package util

func NewStringSet(...string) map[string]struct{} {

// ...

}

func SortStringSet(map[string]struct{}) []string {

// ...

}

客户端的调用方法如下:

golang 复制代码
set := util.NewStringSet("c", "a", "b")

fmt.Println(util.SortStringSet(set))

上述代码的问题是包名 util 没有任何意义,我们可以称它为 common、shared 或 base, 但它仍是一个毫无意义的名称, 无法提供任何关于包的有意义信息。我们应该创建一个富有表现力的包名称,而不是通用工具包(util),例如可以命名为 stringset.

golang 复制代码
package stringset

func New(...string) map[string]struct{} { ... }

func Sort(map[string]struct{}) []string { ... }

上面的程序去掉了 NewStringSet 和 SortStringSet 的后缀,分别变成了 New 和 Sort. 客户端调用代码变成下面的样子。

golang 复制代码
set := stringset.New("c", "a", "b")

fmt.Println(stringset.Sort(set))

NOTE:在Go语言中常见100问题-#12 项目结构最佳实践中,讨论了包的粒度问题,提到了应该避免有几十个包含一两个文件的小包。然而这种小包的思想没有问题,如果一个小的代码组具有很高的内聚性并且不真正属于其他地方,将它组织到一个特定的包中是可以接受的。也就是说包的粒度没有严格规定,找到一个平衡点即可。

解决方法

我们可以对上面的程序做进一步封装,创建一个特定的类型并将Sort作为对外提供的方法,而不是一个对外公开的函数。

golang 复制代码
package stringset

type Set map[string]struct{}

func New(...string) Set { ... }

func (s Set) Sort() []string { ... }

经过上面的重构,使得客户端调用起来更加简单,只有一个对stringset包的引用。

golang 复制代码
set := stringset.New("c", "a", "b")

fmt.Println(set.Sort())

总结

通过上面小的重构,去掉了无意义的包名,对外提供了一个富有表现力的接口。正如 Dave Cheney(Go项目组成员)所说,我们应该合理地找到处理常用程序逻辑的实用程序包。例如,如果有一个客户端和一个服务端包,应该把公共类型放在哪里呢?在这种情况下,也许一种解决方法是将客户端、服务端和公共代码组合放到一个包中。

程序包的命名是应用程序设计的一个关键点,我们应该对此保持谨慎。创建没有意义名称的共享包不是一种好的设计,像 utils、common或base包名称。此外,注意一点,以包提供的内容而不是包含的内容命名包是增加其表现力的有效方法。

相关推荐
clawdashi21 小时前
2026 办公 Agent 选型指南:按场景分五类,一次讲清
前端·人工智能
驳是21 小时前
代码洁癖患者会喜欢的几款 CLI 小工具
前端
Csvn21 小时前
🛋️ requestIdleCallback:把非关键任务塞进浏览器的"空闲时间",告别卡顿
前端
江华森21 小时前
从 fork 到协程:一次完整的操作系统并发编程实操
前端·程序员
四千岁21 小时前
稀疏向量BM25Retriever不支持中文怎么办?jieba来帮忙
前端·javascript·后端
这是个栗子21 小时前
【JS代码分析】前端鉴权基础:Token 的本地存储与状态重置实践
开发语言·前端·javascript
颜进强21 小时前
14 - OpenSpec 老页面改造骨架:定位 + 增量 + 回归三件套
前端·后端·ai编程
做前端的娜娜子21 小时前
async/await 错误处理:try...catch vs .catch() 完全指南
前端·面试·掘金·金石计划
半个落月21 小时前
React 性能优化入门:用 memo 避免无关的子组件重复渲染
前端·react.js
北城笑笑21 小时前
Server 18 ,Nginx + Vite 前端部署排错实战:从 `/api/gpt/chat` 404 到 9012 后端服务的完整定位过程
linux·运维·前端·nginx·ubuntu·vue