当前位置: 首页 > news >正文

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 中.

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • k8s中pod基础及https密钥、horber仓库
  • 修改服务器DNS解析及修改自动对时时区
  • 零信任赋予安全牙齿,AI促使它更锋利
  • JAVA vs Python:谁更适合后端开发?
  • 软件工程-图书管理系统的需求分析
  • 天地图使用
  • WPF MVVM如何在ViewModel直接操作控件对象
  • 详细解说一下Python中的递归和基例
  • JVM面试(二)内存区域划分
  • 在MySQL存储过程中,以下句子需要以分号(;)结尾
  • 后台框架-统一数据格式
  • 网站建设完成后, 做seo必须知道的专业知识之--蜘蛛陷阱
  • 如何防范ddos 攻击
  • 二分查找精炼回顾-kevin
  • 在线压缩pdf,无需安装就可轻易压缩pdf文件
  • ES6指北【2】—— 箭头函数
  • SegmentFault for Android 3.0 发布
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  • 【译】React性能工程(下) -- 深入研究React性能调试
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • 2017-09-12 前端日报
  • Druid 在有赞的实践
  • JS函数式编程 数组部分风格 ES6版
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • Netty+SpringBoot+FastDFS+Html5实现聊天App(六)
  • PHP 7 修改了什么呢 -- 2
  • PHP 程序员也能做的 Java 开发 30分钟使用 netty 轻松打造一个高性能 websocket 服务...
  • Python_网络编程
  • Spring Cloud Alibaba迁移指南(一):一行代码从 Hystrix 迁移到 Sentinel
  • SSH 免密登录
  • 阿里云应用高可用服务公测发布
  • 搭建gitbook 和 访问权限认证
  • 仿天猫超市收藏抛物线动画工具库
  • 基于Vue2全家桶的移动端AppDEMO实现
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 什么软件可以提取视频中的音频制作成手机铃声
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 微信小程序开发问题汇总
  • 06-01 点餐小程序前台界面搭建
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • 正则表达式-基础知识Review
  • ​​快速排序(四)——挖坑法,前后指针法与非递归
  • ​html.parser --- 简单的 HTML 和 XHTML 解析器​
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • ​软考-高级-系统架构设计师教程(清华第2版)【第20章 系统架构设计师论文写作要点(P717~728)-思维导图】​
  • # linux 中使用 visudo 命令,怎么保存退出?
  • #Linux杂记--将Python3的源码编译为.so文件方法与Linux环境下的交叉编译方法
  • (07)Hive——窗口函数详解
  • (1)(1.13) SiK无线电高级配置(六)
  • (1)无线电失控保护(二)
  • (10)工业界推荐系统-小红书推荐场景及内部实践【排序模型的特征】
  • (24)(24.1) FPV和仿真的机载OSD(三)
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (附源码)spring boot车辆管理系统 毕业设计 031034