欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 新车 > Go父类调用子类方法(虚函数调用)

Go父类调用子类方法(虚函数调用)

2025/1/20 5:59:00 来源:https://blog.csdn.net/fishjam/article/details/141726351  浏览:    关键词:Go父类调用子类方法(虚函数调用)

前言

在Go语言中,支持组合而不是继承。网上都说可以通过接口和结构体内嵌来模拟面向对象编程中的子类和父类关系。但给的例子或写法感觉都不是很好,难以达到我的目的(比如通过模板模式实现代码的重用等)。因此调查了一下实现方式。

结论

  • 可以通过定义 interface + 组合接口变量 + NewXxx 函数中对接口变量赋值(类似于给 this 变量赋值) 的方式实现 Go 的继承和虚函数调用。废话少说, 直接上代码。
// 定义一个表示形状的 Shape 接口
type Shape interface {Name() stringArea() float64      //求面积Perimeter() float64 //求周长String() string
}// 定义实现了部分业务逻辑的抽象基类
type BaseShape struct {Shape // 组合了接口,因此有了其接口的方法声明, 注意: 其值为 nil, 需要在子类的 NewXxx 中对其赋值(相当于设置为 this)name  string
}func (bs BaseShape) Name() string {return bs.name
}func (bs BaseShape) AbstractMethod() string {return "base"
}// 此处在 BaseShape 的 String() 方法中调用子类会重载的 Area()/Perimeter() 方法
func (bs BaseShape) String() string {if bs.Shape == nil {// 由于需要手动设置 Shape 的值(不像 C++ 可以在 new 时自动设置), 为了在 nil panic 的时候有更好的提示, 此处主动 panic,提醒这种编码错误panic(fmt.Sprintf("must set Shape in child struct 's new function, name=%s", bs.name))}//这里既可以通过 bs.Xxx() 调用, 也可以通过 bs.Shape.Xxx() 调用.return fmt.Sprintf("name=%s: absMethod=%s, area=%f, perimeter=%f",bs.Name(), bs.AbstractMethod(), bs.Shape.Area(), bs.Shape.Perimeter())
}type Rect struct {BaseShapeWidth  float64Height float64
}func (r *Rect) Area() float64 {return r.Width * r.Height
}
func (r *Rect) Perimeter() float64 {return 2 * (r.Width + r.Height)
}func (r *Rect) AbstractMethod() string {return "abs_rect"
}func NewRect(width, height float64) *Rect {rect := &Rect{BaseShape: BaseShape{name: "rect"},Width:     width,Height:    height,}rect.Shape = rect // 将 BaseShape 中的 Shape 变量设置为 this,父类才可以调用return rect
}type Square struct {Rect
}func NewSquare(sides float64) *Square {square := &Square{Rect{BaseShape: BaseShape{name: "square"},Height:    sides,Width:     sides,}}square.Shape = squarereturn square
}
type Circle struct {BaseShapeRadius float64
}func (c *Circle) Area() float64 {return math.Pi * c.Radius * c.Radius
}
func (c *Circle) Perimeter() float64 {return 2 * math.Pi * c.Radius
}func (c *Circle) AbstractMethod() string {return "abs_circle"
}func NewCircle(radius float64) *Circle {circle := &Circle{BaseShape: BaseShape{name: "circle"},Radius:    radius,}circle.Shape = circle //将 BaseShape 中的 Shape 变量设置为 this,父类才可以调用return circle
}

以上代码实现了如下的继承结构,可以将 String() 方法看成业务方法,其中调用子类提供实现的抽象方法。
在这里插入图片描述

测试代码

通过 shape 变量的 String() 方法调用了子类的 Name()/Area()/Perimeter() 等方法.

func TestVirtualFunctionCall(t *testing.T) {var rect Shape = NewRect(10, 20)var circle Shape = NewCircle(10)var square Shape = NewSquare(10)assert.Equal(t, "name=rect: absMethod=base, area=200.000000, perimeter=60.000000", rect.String())assert.Equal(t, "name=square: absMethod=base, area=100.000000, perimeter=40.000000", square.String())assert.Equal(t, "name=circle: absMethod=base, area=314.159265, perimeter=62.831853", circle.String())
}

补充说明

  • 通过这种方法,可以成功的在 Go 语言中实现 多态,虚函数 等 C++/Java 才有的概念,从而可以想办法实现各种设计模式,大大提高项目质量。
  • 但也存在一些限制,比如:
    • 无法在 BaseShape 中再次定义一些虚函数由子类实现并调用(参考 AbstractMethod),如想要其也是虚函数的样子,必须也定义到 interface 中.

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com