每个模块应该只做一件事,并把它做好:
// 好的设计:职责单一
github.com/example/validator // 只负责验证
github.com/example/logger // 只负责日志
github.com/example/cache // 只负责缓存
// 不好的设计:职责混乱
github.com/example/utils // 包含各种不相关的功能
只暴露必要的接口,隐藏实现细节:
package cache
// Cache 公开的接口
type Cache interface {
Get(key string) (interface{}, bool)
Set(key string, value interface{})
Delete(key string)
}
// NewCache 公开的构造函数
func NewCache() Cache {
return &memoryCache{
data: make(map[string]interface{}),
}
}
// memoryCache 私有的实现
type memoryCache struct {
data map[string]interface{}
mu sync.RWMutex
}
func (c *memoryCache) Get(key string) (interface{}, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
val, ok := c.data[key]
return val, ok
}
func (c *memoryCache) Set(key string, value interface{}) {
c.mu.Lock()
defer c.mu.Unlock()
c.data[key] = value
}
func (c *memoryCache) Delete(key string) {
c.mu.Lock()
defer c.mu.Unlock()
delete(c.data, key)
}
提供灵活的配置选项:
package cache
type Config struct {
MaxSize int
DefaultTTL time.Duration
OnEvict func(key string, value interface{})
}
type Option func(*Config)
func WithMaxSize(size int) Option {
return func(c *Config) {
c.MaxSize = size
}
}
func WithDefaultTTL(ttl time.Duration) Option {
return func(c *Config) {
c.DefaultTTL = ttl
}
}
func WithOnEvict(fn func(key string, value interface{})) Option {
return func(c *Config) {
c.OnEvict = fn
}
}
func NewCache(opts ...Option) Cache {
config := &Config{
MaxSize: 1000,
DefaultTTL: 0,
}
for _, opt := range opts {
opt(config)
}
return &memoryCache{
config: config,
data: make(map[string]interface{}),
}
}
myproject/
├── cmd/ # 主应用程序
│ └── myapp/
│ └── main.go
├── internal/ # 私有代码
│ └── service/
│ └── user.go
├── pkg/ # 公开代码
│ └── utils/
│ └── string.go
├── api/ # API 定义
│ └── v1/
│ └── user.proto
├── configs/ # 配置文件
│ └── config.yaml
├── docs/ # 文档
│ └── README.md
├── examples/ # 示例代码
│ └── example.go
├── go.mod
├── go.sum
├── Makefile
└── README.md
mylib/
├── cache.go # 主要功能
├── cache_test.go # 测试文件
├── options.go # 配置选项
├── internal/ # 私有代码
│ └── store/
│ └── memory.go
├── examples/ # 示例
│ └── basic/
│ └── main.go
├── go.mod
├── go.sum
├── LICENSE
└── README.md
# MyCache
一个简单高效的 Go 内存缓存库。
## 特性
- 支持过期时间
- 支持最大容量限制
- 线程安全
- 支持淘汰回调
## 安装
```bash
go get github.com/example/mycache
package main
import (
"fmt"
"github.com/example/mycache"
)
func main() {
cache := mycache.NewCache(
mycache.WithMaxSize(100),
mycache.WithDefaultTTL(5 * time.Minute),
)
cache.Set("key", "value")
if val, ok := cache.Get("key"); ok {
fmt.Println(val)
}
}
| 选项 | 说明 | 默认值 |
|---|---|---|
| WithMaxSize | 最大容量 | 1000 |
| WithDefaultTTL | 默认过期时间 | 0(永不过期) |
| WithOnEvict | 淘汰回调 | nil |
完整文档请访问 GoDoc
MIT License
### 代码注释
```go
// Package cache 提供了一个线程安全的内存缓存实现。
//
// 支持特性:
// - 过期时间
// - 最大容量限制
// - 淘汰回调
//
// 示例:
//
// cache := cache.NewCache(
// cache.WithMaxSize(100),
// cache.WithDefaultTTL(5 * time.Minute),
// )
// cache.Set("key", "value")
// value, ok := cache.Get("key")
package cache
// Cache 定义了缓存的基本接口。
type Cache interface {
// Get 获取缓存值。
// 如果键不存在或已过期,返回 nil 和 false。
Get(key string) (interface{}, bool)
// Set 设置缓存值。
// 使用默认的过期时间。
Set(key string, value interface{})
// SetWithTTL 设置缓存值并指定过期时间。
SetWithTTL(key string, value interface{}, ttl time.Duration)
// Delete 删除缓存值。
Delete(key string)
// Clear 清空所有缓存。
Clear()
}
// NewCache 创建一个新的缓存实例。
//
// 可选配置:
// - WithMaxSize(int): 设置最大容量
// - WithDefaultTTL(time.Duration): 设置默认过期时间
// - WithOnEvict(func): 设置淘汰回调函数
func NewCache(opts ...Option) Cache {
// ...
}
vMAJOR.MINOR.PATCH
MAJOR: 不兼容的 API 变更
MINOR: 向后兼容的功能新增
PATCH: 向后兼容的问题修复
# 创建 v1.0.0 标签
git tag v1.0.0
git push origin v1.0.0
# 创建 v1.1.0 标签
git tag v1.1.0
git push origin v1.1.0
# 创建 v2.0.0 标签
git tag v2.0.0
git push origin v2.0.0
// v1/go.mod
module github.com/example/mylib
// v2/go.mod
module github.com/example/mylib/v2
或者使用主分支:
// go.mod (v2+)
module github.com/example/mylib/v2
go 1.21
# 确保代码质量
go test ./...
go vet ./...
# 更新文档
# 更新 README.md
# 更新 CHANGELOG.md
# 整理依赖
go mod tidy
# 创建带注释的标签
git tag -a v1.0.0 -m "Release v1.0.0"
# 推送标签
git push origin v1.0.0
# 在另一个目录测试
mkdir test && cd test
go mod init test
go get github.com/example/mylib@v1.0.0
# Changelog
All notable changes to this project will be documented in this file.
## [1.1.0] - 2024-01-15
### Added
- 新增 `SetWithTTL` 方法
- 支持淘汰回调函数
### Changed
- 优化内存使用
### Fixed
- 修复并发写入时的竞态条件
## [1.0.0] - 2024-01-01
### Added
- 初始版本
- 基本的缓存功能
- 支持过期时间
# 1. 创建项目
mkdir stringutils && cd stringutils
# 2. 初始化模块
go mod init github.com/example/stringutils
# 3. 创建代码
cat > stringutils.go << 'EOF'
package stringutils
// Reverse 反转字符串
func Reverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
// IsEmpty 检查字符串是否为空
func IsEmpty(s string) bool {
return len(s) == 0
}
// IsBlank 检查字符串是否为空白
func IsBlank(s string) bool {
for _, r := range s {
if !unicode.IsSpace(r) {
return false
}
}
return true
}
// Truncate 截断字符串
func Truncate(s string, maxLen int) string {
if len(s) <= maxLen {
return s
}
return s[:maxLen] + "..."
}
EOF
# 4. 创建测试
cat > stringutils_test.go << 'EOF'
package stringutils
import "testing"
func TestReverse(t *testing.T) {
tests := []struct {
input string
expected string
}{
{"hello", "olleh"},
{"", ""},
{"a", "a"},
{"你好", "好你"},
}
for _, tt := range tests {
if got := Reverse(tt.input); got != tt.expected {
t.Errorf("Reverse(%q) = %q, want %q", tt.input, got, tt.expected)
}
}
}
func TestIsEmpty(t *testing.T) {
if !IsEmpty("") {
t.Error("IsEmpty(\"\") should be true")
}
if IsEmpty("a") {
t.Error("IsEmpty(\"a\") should be false")
}
}
EOF
# 5. 运行测试
go test ./...
# 6. 创建 README
cat > README.md << 'EOF'
# StringUtils
Go 字符串工具库。
## 安装
```bash
go get github.com/example/stringutils
import "github.com/example/stringutils"
func main() {
s := stringutils.Reverse("hello") // "olleh"
empty := stringutils.IsEmpty("") // true
}
EOF
git init git add . git commit -m "Initial commit"
git tag v1.0.0
git remote add origin https://github.com/example/stringutils.git git push -u origin main git push origin v1.0.0
## 32.8 小结
本章详细讲解了如何创建和发布 Go 模块:
1. **设计原则**:单一职责、最小化 API、可配置性
2. **项目结构**:标准项目结构和库项目结构
3. **文档编写**:README 和代码注释
4. **版本管理**:语义化版本和 Git 标签
5. **发布流程**:准备、创建标签、验证发布
创建高质量的 Go 模块需要良好的设计、完善的文档和规范的发布流程。在下一章中,我们将学习文件与 IO 操作。