Golang中组合的正确使用方式及两种实现写法的差异
Go组合写法差异解答
首先明确:你提供的两种写法都是合法可运行的Go代码,不存在绝对的对错之分,二者的差异完全是依赖注入方式和实例共享逻辑的区别,适配不同的业务场景。另外先补充一点:你当前的写法属于「具名字段组合」,如果要实现Go特有的、类似OOP继承的方法提升效果,推荐改用匿名嵌入类型的写法,后面会给出示例。
两种写法的核心差异
第一种:每个子类内部独立初始化BaseClass
每个ChildrenClass1、ChildrenClass2的实例都会持有完全独立的BaseClass实例,哪怕你传入的是同一个db连接,两个子类持有的BaseClass也是内存地址不同的独立对象。
- 适用场景:BaseClass中除了db这类全局共享的依赖外,还有会被单个实例修改的可变状态(比如计数器、临时缓存等),需要隔离不同子类实例的状态、避免互相影响时选这种。
- 劣势:如果BaseClass初始化成本很高,重复创建会产生不必要的资源开销,也无法统一批量修改所有子类依赖的BaseClass配置。
第二种:外部统一初始化BaseClass后注入给所有子类
所有ChildrenClass1、ChildrenClass2的实例共享同一个BaseClass实例,chilrend1和chilrend2中的baseClass指针指向同一块内存地址。
- 适用场景:BaseClass只有不可变的只读依赖(比如示例中的db连接,本身就是线程安全、全局复用的对象),没有其他可变状态,这种写法更节省资源,也可以统一配置BaseClass的属性后批量传递给所有子类。
- 劣势:如果BaseClass存在可变属性,任意一个子类修改了BaseClass的属性,会影响所有持有这个实例的其他对象,并发场景下容易出现线程安全问题。
Go组合规范写法建议
如果是要通过组合实现公共方法复用(类似OOP的继承效果),推荐改用Go的匿名嵌入类型写法,不需要写具名的baseClass字段,即可实现方法提升:
type BaseClass struct { db *DB } // 公共方法,嵌入BaseClass的子类可以直接调用 func (b *BaseClass) CommonQuery(sql string) error { return b.db.Exec(sql) } type ChildrenClass1 struct { *BaseClass // 匿名嵌入BaseClass指针 // 此处添加ChildrenClass1独有的字段 } type ChildrenClass2 struct { *BaseClass // 匿名嵌入BaseClass指针 // 此处添加ChildrenClass2独有的字段 } // 构造函数可以根据业务需求选择两种初始化逻辑,这里以共享BaseClass的场景为例 func NewChildrenClass1(base *BaseClass) *ChildrenClass1 { return &ChildrenClass1{ BaseClass: base, } } func NewChildrenClass2(base *BaseClass) *ChildrenClass2 { return &ChildrenClass2{ BaseClass: base, } } func main(){ db := NewDB() baseClass := NewBaseClass(db) children1 := NewChildrenClass1(baseClass) children2 := NewChildrenClass2(baseClass) // 可以直接调用BaseClass的方法,不需要逐层访问base字段 _ = children1.CommonQuery("select 1") _ = children2.CommonQuery("select 2") }
内容的提问来源于stack exchange,提问作者Hung Pham
相关产品推荐
相关产品推荐

