# [一个有趣的bug] golang 组件化编程
## 组建化简述
* 众所周知, golang以组件化代替继承,这种组件化的思想有些类似于cpp的多继承(ps: 比多继承要好理解的多, 读者大可放心), 而多继承呢就会有一些经典的问题, 重定义和菱形继承, golang的组件化也存在这样类似的问题, 本文就该问题进行探讨。
* 重定义: cpp中的多继承,当类继承的两个父类包含名称相同的方法或属性, 则触发重定义, go语言中也存在相同的问题
```go
type Cat struct {
}
func (c *Cat)Eat(){
}
type Dog struct{
}
func (d *Dog)Eat(){
}
type CatDog struct {
Dog
Cat
}
func test(){
cd := new(CatDog)
//cd.Eat() //编译器无法确定调用的是Cat还是Dog的Eat()
//需要明确指出
cd.Dog.Eat()
cd.Cat.Eat()
}
```
不过组件化还是和继承完全不一样的如下代码
```go
type Cat struct {
}
func (c *Cat)Eat(){
fmt.Println("cat eat")
}
type Dog struct{
}
func (d *Dog)Eat(){
fmt.Println("dog eat")
}
type ErHa struct {
Dog
}
type CatDog struct {
ErHa
Cat
}
func test(){
cd := new(CatDog)
/**
* 虽然包含的两个组件都拥有Eat方法,但是层级不同,
* ErHa的层级会比Cat深一层(说法可能不太正确,但确实可以描述问题),
* 编译器会帮你调用到层级少的那个组件的Eat方法
*/
cd.Eat() //此时调用Cat组件的Eat方法
}
```
## 实际问题
```go
type AnimalAbstract interface {
Eat()
Run()
}
type Animal struct {
}
func (a *Animal)Eat(){
fmt.Println("animal eat!")
}
func (a *Animal)Run(){
fmt.Println("animal run!")
}
type Mammal struct {
Animal
power map[string]string
}
func (m *Mammal) Eat(){
fmt.Println("mammal eat!")
m.power = make(map[string]string)
}
type Monkey struct {
Mammal
}
func (m *Monkey)Run(){
fmt.Println("monkey run")
m.power["run"] = "run"
}
type People1 struct {
Mammal
}
type People2 struct {
Monkey
}
type People3 struct {
Mammal
Monkey
}
func Exec(a AnimalAbstract){
/**
* 传入People3时会报错,因为使用接口调用Eat()时实际调用到Mammal组件的Eat(),
* 而调用Run时则实际调用到Monkey组件的Run(), 而Monkey组件的power并为初始化,
* 所以触发panic, 在cpp中这种写法有重定义限制所以你可以很容易注意到这个问题,
* 也可以使用虚继承来解决问题, 而golang则完全开放,把问题交给的程序员,
* 如果不注意确实会踩坑(事例代码有些不切实际, 不过笔者在工作中确实碰到了这个问题,
* 具体代码不方便展出)
*/
a.Eat()
a.Run()
fmt.Println("==========================")
}
func main(){
Exec(new(People1))
Exec(new(People2))
//People3会报错
Exec(new(People3))
}
```
有疑问加站长微信联系(非本文作者))