无接口与函数重写场景下GoLang代码复用方法咨询
Go 无继承场景下的代码复用方案(Apple/Orange 处理逻辑重构)
针对你的场景,Go中推荐通过接口抽象通用行为+组合复用的方式实现代码复用,完全不需要依赖继承或函数覆盖。以下是具体重构步骤:
1. 定义通用接口抽象Fruit行为
首先在models.go中新增一个接口,统一Apple和Orange的通用操作,同时预留专属逻辑的扩展点:
package project import "errors" type Fruit struct { Property1 int64 Property2 int64 Property3 int64 Property4 int64 Property5 int64 } type Apple struct { Colour int64 Fruit } type Orange struct { Fruit } // Fruitable 定义水果的通用行为 type Fruitable interface { // GetFruit 获取通用的Fruit属性 GetFruit() *Fruit // ValidateExtra 验证专属属性(Apple的Colour,Orange无则返回nil) ValidateExtra() error } // Apple实现Fruitable接口 func (a *Apple) GetFruit() *Fruit { return &a.Fruit } func (a *Apple) ValidateExtra() error { // Apple专属的Colour验证逻辑 if a.Colour <= 0 { return errors.New("apple colour must be positive") } return nil } // Orange实现Fruitable接口 func (o *Orange) GetFruit() *Fruit { return &o.Fruit } func (o *Orange) ValidateExtra() error { // Orange无专属属性验证,直接返回nil return nil }
2. 重构通用验证逻辑
将validateApples和validateOranges中的重复逻辑抽离为通用函数,仅通过接口处理差异部分:
package handler import ( "errors" "project" ) // validateFruits 通用水果验证逻辑,处理Fruit公共属性+调用专属验证 func validateFruits(fruits []project.Fruitable) error { if len(fruits) == 0 { return errors.New("fruit list cannot be empty") } for _, f := range fruits { // 验证Fruit公共属性(原两个验证函数中的重复代码) fruit := f.GetFruit() if fruit.Property1 <= 0 { return errors.New("property1 must be positive") } if fruit.Property2 <= 0 { return errors.New("property2 must be positive") } // ... 依次添加Property3-5的验证逻辑 // 调用专属属性验证 if err := f.ValidateExtra(); err != nil { return err } } return nil } // 保留原入口函数,仅做类型转换后调用通用验证 func validateApples(apples []project.Apple) error { fruits := make([]project.Fruitable, len(apples)) for i, a := range apples { fruits[i] = &a } return validateFruits(fruits) } func validateOranges(oranges []project.Orange) error { fruits := make([]project.Fruitable, len(oranges)) for i, o := range oranges { fruits[i] = &o } return validateFruits(fruits) }
3. Handler层复用业务逻辑
如果ApplesPost和OrangesPost中的后续业务逻辑(比如调用service、repository)也高度重复,可以进一步抽离通用处理函数:
package handler import "project" type Handler struct { // 假设你的Handler依赖的service、repo等实例 } // processFruits 通用业务处理逻辑,复用原ApplesPost/OrangesPost中的重复代码 func (h Handler) processFruits(fruits []project.Fruitable) error { // 这里放置原两个Post函数中的"more code"部分 // 比如调用service层处理水果数据、操作数据库等 return nil } func (h Handler) ApplesPost(apples []project.Apple) { // 类型转换+通用验证 fruits := make([]project.Fruitable, len(apples)) for i, a := range apples { fruits[i] = &a } if err := validateFruits(fruits); err != nil { // 处理错误(比如返回HTTP 400) return } // 调用通用业务逻辑 if err := h.processFruits(fruits); err != nil { // 处理业务错误 return } } func (h Handler) OrangesPost(oranges []project.Orange) { fruits := make([]project.Fruitable, len(oranges)) for i, o := range oranges { fruits[i] = &o } if err := validateFruits(fruits); err != nil { // 处理错误 return } // 调用通用业务逻辑 if err := h.processFruits(fruits); err != nil { // 处理业务错误 return } }
4. Service/Repository层复用思路
对于service和repository层,同样可以用接口抽象通用操作:
- 定义
FruitService接口,包含SaveFruits等通用方法 - 让
AppleService和OrangeService实现该接口,或直接编写通用函数接收[]Fruitable参数 - 数据库操作层面,可通过
GetFruit()获取公共属性复用基础CRUD逻辑,专属属性单独处理
这种方案完全符合Go的设计哲学,通过接口组合而非继承实现多态,既避免了代码重复,又保留了各类型的专属逻辑扩展性。
内容的提问来源于stack exchange,提问作者Yalda
相关产品推荐
相关产品推荐

