没有 class 和继承,Go 怎么写业务?struct + 组合实战 📅 2026/7/19 4:01:29 本文为《Java工程师转Go实战》连载第 5 篇 / 共 20 篇上一篇指针与内存模型下一篇从 Java interface 到 Go 鸭子类型类比开场Java 写业务class OrderService extends BaseService层层继承。Go 的态度组合优于继承。没有extends用struct 嵌套embedding复用能力。一、用 struct 表达领域对象JavapublicclassOrder{privateLongid;privateBigDecimalamount;privateListOrderItemitems;// getter/setter...}GotypeOrderItemstruct{SKUstringPriceint64// 分避免 float 金额}typeOrderstruct{IDint64Amountint64Items[]OrderItem}没有 getter/setter 强迫症同包内直接访问字段跨包用首字母大小写控制导出。二、用组合代替继承Java 继承publicclassBaseEntity{protectedLocalDateTimecreatedAt;protectedLocalDateTimeupdatedAt;}publicclassUserextendsBaseEntity{privateStringname;}Go 嵌入typeTimestampsstruct{CreatedAt time.Time UpdatedAt time.Time}typeUserstruct{Timestamps// 匿名字段嵌入IDint64Namestring}funcmain(){u:User{Name:张三}u.CreatedAttime.Now()// 可直接访问嵌入字段}嵌入不是继承User不是Timestamps的子类型只是「自带这些字段和方法」。三、Service 层怎么写JavaSpringServicepublicclassUserService{AutowiredprivateUserRepositoryuserRepository;publicUsercreate(UserDTOdto){...}}Go手写依赖注入typeUserRepositoryinterface{Save(ctx context.Context,u*User)errorFindByID(ctx context.Context,idint64)(*User,error)}typeUserServicestruct{repo UserRepository}funcNewUserService(repo UserRepository)*UserService{returnUserService{repo:repo}}func(s*UserService)Create(ctx context.Context,namestring)(*User,error){u:User{Name:name,ID:time.Now().UnixNano()}iferr:s.repo.Save(ctx,u);err!nil{returnnil,err}returnu,nil}对照NewUserService≈ 构造器注入interface定义在 consumer 侧很常见第 6 篇细讲没有Autowired显式传依赖反而更清晰四、Builder 模式还要吗Java 里 LombokBuilder很香Go 常见写法typeServerConfigstruct{HoststringPortint}typeServerOptionfunc(*ServerConfig)funcWithPort(portint)ServerOption{returnfunc(c*ServerConfig){c.Portport}}funcNewServer(opts...ServerOption)*ServerConfig{cfg:ServerConfig{Host:0.0.0.0,Port:8080}for_,opt:rangeopts{opt(cfg)}returncfg}函数式 Option 在 Go 标准库和 gRPC 里非常常见。五、完整小例子订单创建packageorderimportcontexttypeOrderstruct{IDint64UserIDint64Amountint64}typeRepositoryinterface{Insert(ctx context.Context,o*Order)error}typeServicestruct{repo Repository}funcNewService(repo Repository)*Service{returnService{repo:repo}}func(s*Service)PlaceOrder(ctx context.Context,userID,amountint64)(*Order,error){ifamount0{returnnil,ErrInvalidAmount}o:Order{UserID:userID,Amount:amount,ID:generateID()}iferr:s.repo.Insert(ctx,o);err!nil{returnnil,err}returno,nil}测试时Repository换 mock 实现即可和 Java 里 mockUserRepository一样。 面试追问Go 有没有继承没有类型继承。用 struct embedding 复用字段和方法是组合不是 is-a 关系。embedding 后方法冲突怎么办外层显式定义同名方法覆盖调用时需写清outer.Inner.Method()。Go 怎么做 DDD 聚合根大 struct 包级私有字段小写 公开方法维护不变量和 Java 封装思路一致。 一句话总结别在 Go 里找 class——用 struct 建模、用 interface 抽象、用组合复用Spring 那套分层思维原样适用。建议标签GolangJava面向对象struct后端