[一个有趣的bug] golang 组件化编程

loop_0 · 2020-04-28 15:06:05 · 1188 次点击 · 预计阅读时间 2 分钟 · 大约8小时之前 开始浏览    
这是一个创建于 2020-04-28 15:06:05 的文章,其中的信息可能已经有所发展或是发生改变。

[一个有趣的bug] golang 组件化编程

组建化简述

  • 众所周知, golang以组件化代替继承,这种组件化的思想有些类似于cpp的多继承(ps: 比多继承要好理解的多, 读者大可放心), 而多继承呢就会有一些经典的问题, 重定义和菱形继承, golang的组件化也存在这样类似的问题, 本文就该问题进行探讨。
  • 重定义: cpp中的多继承,当类继承的两个父类包含名称相同的方法或属性, 则触发重定义, 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()
}

不过组件化还是和继承完全不一样的如下代码

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方法
}

实际问题

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))

}

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1188 次点击  
加入收藏 微博
2 回复  |  直到 2020-04-30 11:51:43
sdghchj
sdghchj · #1 · 5年之前

聚合不是继承 聚合不是继承 聚合不是继承

loop_0
loop_0 · #2 · 5年之前

@sdghchj 是的, 文中也明确写了"组件化的编程思想", 只是使用的cpp的多继承来对比引出问题而已

添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传