10 - 结构体:字段、方法、嵌入、组合
10 - 结构体
10.1 结构体基础
package main
import "fmt"
// 声明结构体
type User struct {
ID int
Name string
Email string
Age int
IsActive bool
}
func main() {
// 创建方式一:字段名赋值
u1 := User{
ID: 1,
Name: "Alice",
Email: "[email protected]",
Age: 30,
IsActive: true,
}
// 创建方式二:按顺序赋值(不推荐,字段顺序变化会出错)
u2 := User{2, "Bob", "[email protected]", 25, true}
// 创建方式三:部分字段(其余为零值)
u3 := User{Name: "Carol", Email: "[email protected]"}
// 创建方式四:new(返回指针)
u4 := new(User)
u4.Name = "Dave"
// 创建方式五:&User{...}
u5 := &User{Name: "Eve"}
fmt.Printf("%+v\n", u1)
fmt.Printf("%+v\n", u2)
fmt.Printf("%+v\n", u3)
fmt.Printf("%+v\n", u4)
fmt.Printf("%+v\n", u5)
}
10.2 字段标签(Tag)
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type User struct {
ID int `json:"id" db:"id" validate:"required"`
Name string `json:"name" db:"name" validate:"required,min=2"`
Email string `json:"email" db:"email" validate:"required,email"`
Password string `json:"-" db:"password"` // json:"-" 表示忽略
Age int `json:"age,omitempty"` // 零值时忽略
}
func main() {
u := User{
ID: 1,
Name: "Alice",
Email: "[email protected]",
Password: "secret",
Age: 0, // 零值会被忽略
}
// JSON 序列化
data, _ := json.MarshalIndent(u, "", " ")
fmt.Println(string(data))
// {"id":1,"name":"Alice","email":"[email protected]"}
// 反射读取 tag
t := reflect.TypeOf(u)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fmt.Printf("%s: json=%s, db=%s\n",
field.Name,
field.Tag.Get("json"),
field.Tag.Get("db"),
)
}
}
10.3 结构体方法
package main
import (
"fmt"
"math"
)
type Point struct {
X, Y float64
}
// 值接收者(不修改原值)
func (p Point) Distance(q Point) float64 {
return math.Hypot(q.X-p.X, q.Y-p.Y)
}
func (p Point) String() string {
return fmt.Sprintf("(%.1f, %.1f)", p.X, p.Y)
}
// 指针接收者(可以修改原值)
func (p *Point) Translate(dx, dy float64) {
p.X += dx
p.Y += dy
}
func (p *Point) Scale(factor float64) {
p.X *= factor
p.Y *= factor
}
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)
}
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
func main() {
p1 := Point{0, 0}
p2 := Point{3, 4}
fmt.Printf("距离: %.1f\n", p1.Distance(p2)) // 5.0
p1.Translate(10, 20)
fmt.Println("平移后:", p1) // (10.0, 20.0)
rect := Rectangle{Width: 10, Height: 5}
fmt.Printf("面积: %.1f, 周长: %.1f\n", rect.Area(), rect.Perimeter())
rect.Scale(2)
fmt.Printf("缩放后: 面积=%.1f\n", rect.Area())
}
值接收者 vs 指针接收者
| 特性 | 值接收者 func (T) | 指针接收者 func (*T) |
|---|---|---|
| 修改接收者 | ❌ 不能 | ✅ 可以 |
| 调用者类型 | T 或 *T 都可调用 | T 或 *T 都可调用 |
| 并发安全 | 每次调用独立副本 | 共享数据,需加锁 |
| 性能 | 大结构体有拷贝开销 | 零拷贝 |
💡 选择规则:
- 需要修改接收者 → 指针接收者
- 结构体很大 → 指针接收者
- 结构体包含
sync.Mutex→ 指针接收者 - 一致性:同一类型的所有方法用相同接收者类型
10.4 结构体嵌入(Embedding)
Go 通过嵌入实现组合,而非继承。
package main
import "fmt"
type Address struct {
City string
Country string
}
func (a Address) FullAddress() string {
return a.City + ", " + a.Country
}
type Company struct {
Name string
Address // 嵌入(匿名字段)
}
type Employee struct {
Name string
Salary float64
Address // 嵌入
Company // 嵌入
}
func main() {
e := Employee{
Name: "Alice",
Salary: 100000,
Address: Address{
City: "Beijing",
Country: "China",
},
Company: Company{
Name: "TechCorp",
Address: Address{
City: "Shanghai",
Country: "China",
},
},
}
// 直接访问嵌入字段
fmt.Println(e.City) // Beijing(提升的字段)
// 显式访问
fmt.Println(e.Address.City) // Beijing
fmt.Println(e.Company.City) // Shanghai
// 嵌入的方法也被提升
fmt.Println(e.Address.FullAddress()) // Beijing, China
}
嵌入接口
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
}
// 结构体嵌入接口
type Logger struct {
io.Writer // 嵌入接口
prefix string
}
func (l *Logger) Log(msg string) {
l.Write([]byte(l.prefix + msg + "\n"))
}
10.5 构造函数模式
Go 没有构造函数关键字,用 New 函数模拟:
package main
import "errors"
type Config struct {
Host string
Port int
MaxConns int
Debug bool
}
// Option 函数模式
type Option func(*Config)
func WithPort(port int) Option {
return func(c *Config) {
c.Port = port
}
}
func WithMaxConns(n int) Option {
return func(c *Config) {
c.MaxConns = n
}
}
func WithDebug(debug bool) Option {
return func(c *Config) {
c.Debug = debug
}
}
// NewConfig 使用 Option 模式
func NewConfig(host string, opts ...Option) (*Config, error) {
if host == "" {
return nil, errors.New("host is required")
}
// 默认值
c := &Config{
Host: host,
Port: 8080,
MaxConns: 100,
Debug: false,
}
for _, opt := range opts {
opt(c)
}
return c, nil
}
func main() {
cfg, err := NewConfig("localhost",
WithPort(3000),
WithMaxConns(200),
WithDebug(true),
)
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", cfg)
}
10.6 结构体比较和复制
func main() {
// 结构体比较(所有字段可比较时)
type Point struct{ X, Y int }
p1 := Point{1, 2}
p2 := Point{1, 2}
fmt.Println(p1 == p2) // true
// 值复制
p3 := p1
p3.X = 100
fmt.Println(p1) // {1 2}(不受影响)
// 深拷贝(包含引用类型字段时)
type Data struct {
Name string
Tags []string
}
d1 := Data{Name: "test", Tags: []string{"a", "b"}}
d2 := d1
d2.Tags[0] = "modified"
fmt.Println(d1.Tags) // [modified b](受影响!slice 共享底层数组)
// 深拷贝
d3 := Data{
Name: d1.Name,
Tags: make([]string, len(d1.Tags)),
}
copy(d3.Tags, d1.Tags)
d3.Tags[0] = "independent"
fmt.Println(d1.Tags) // [modified b]
fmt.Println(d3.Tags) // [independent b]
}
10.7 空结构体
func main() {
// 空结构体不占内存
var s struct{}
fmt.Println(unsafe.Sizeof(s)) // 0
// 用作集合(不关心值,只关心键存在性)
set := make(map[string]struct{})
set["a"] = struct{}{}
set["b"] = struct{}{}
if _, ok := set["a"]; ok {
fmt.Println("a 存在")
}
// channel 仅作信号通知
done := make(chan struct{})
go func() {
fmt.Println("工作完成")
done <- struct{}{}
}()
<-done
// 方法接收者(无状态)
type Validator struct{}
func (v Validator) Validate(s string) bool {
return len(s) > 0
}
}
10.8 结构体组合设计模式
// 装饰器模式
type Logger struct {
next http.Handler
}
func (l *Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) {
start := time.Now()
l.next.ServeHTTP(w, r)
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
}
// 中间件链
type Chain struct {
handlers []http.Handler
}
// 仓储模式
type UserRepository struct {
db *sql.DB
}
func (r *UserRepository) FindByID(id int) (*User, error) {
// 查询数据库
}
func (r *UserRepository) Save(user *User) error {
// 保存到数据库
}
🏢 业务场景
- 数据模型:ORM 模型定义,JSON 序列化
- 配置管理:Option 模式构建灵活配置
- 中间件:嵌入 + 组合实现 HTTP 中间件链
- 领域对象:结构体方法封装业务逻辑
- 仓储模式:结构体封装数据访问