【Go语言从入门到实战】面向对象编程篇
面向对象编程
Go语言的面向对象编程和其他语言有非常大的差别。
Go 是一种面向对象的语言吗?
是和不是。虽然 Go 有类型和方法,并允许面向对象的编程风格,但没有类型层次结构(继承)。Go 中的“接口”概念提供了一种不同的方法,我们认为这种方法易于使用,并且在某些方面更通用。还有一些方法可以将类型嵌入到其他类型中,以提供类似于(但不完全相同)子类化的东西。此外,Go 中的方法比 C++ 或 Java 中的方法更通用:它们可以为任何类型的数据定义,甚至是内置类型,例如普通的“未装箱”整数。它们不限于结构(类)。
此外,缺少类型层次结构使得 Go 中的“对象”感觉比 C++ 或 Java 等语言中的“对象”轻量级得多。
封装数据和行为
结构体定义
实例创建及初始化
type Employee struct {Id stringName stringAge int
}func TestCreateEmployeeObj(t *testing.T) {e := Employee{"0", "Bob", 20}e1 := Employee{Name: "Mike", Age: 30}e2 := new(Employee) // 返回指针e2.Id = "2"e2.Name = "Rose"e2.Age = 22t.Log(e)t.Log(e1)t.Log(e1.Id)t.Log(e2)t.Logf("e is %T", e)t.Logf("e2 is %T", e2)
}
行为(方法)定义
-
第一种
func (e Employee) String() string {fmt.Printf("Address is %x", unsafe.Pointer(&e.Name))fmt.Println()return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age) }func TestStructOperations(t *testing.T) {e := Employee{"0", "Bob", 20}fmt.Printf("Address is %x", unsafe.Pointer(&e.Name))fmt.Println()t.Log(e.String()) }
所以这种写法会有复制的开销。
-
第二种
func (e *Employee) String() string {fmt.Printf("Address is %x", unsafe.Pointer(&e.Name))fmt.Println()return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age) }func TestStructOperations(t *testing.T) {e := &Employee{"0", "Bob", 20} // 传递引用fmt.Printf("Address is %x", unsafe.Pointer(&e.Name))fmt.Println()t.Log(e.String()) }
更推荐这种。
接口(定义交互协议)
Go的接口和很多主流编程语言的接口有很大的区别。
Java代码的示例:
Duck Type式接口
Go语言的interface:
type Programmer interface {WriteHelloWorld() string
}type GoProgrammer struct {
}func (g *GoProgrammer) WriteHelloWorld() string { // duck type 鸭子类型return "Hello World"
}func TestClient(t *testing.T) {var p Programmerp = new(GoProgrammer)t.Log(p.WriteHelloWorld())
}
Go接口
接口变量
自定义类型
type IntConv func(op int) int// 计算函数操作的时长
func timeSpend(inner IntConv) IntConv { // 以前的方法特别的长 我们可以用自定义类型做替换// 类似装饰者模式,对原来的函数进行了一层包装return func(n int) int {start := time.Now()ret := inner(n)fmt.Println("time spend:", time.Since(start).Seconds())return ret}
}
扩展和复用(类似继承)
Go语言无法天然支持继承,但是又想要实现面向对象的特性。
即父类对象 使用子类对象初始化,那么该父类对象调用的函数就是子类实现的函数 ,从而满足LSP(子类交换原则)。
案例一: Go语言 支持扩展父类的功能,如下代码:
package oriented_testimport ("fmt""testing"
)// Pet 类
type Pet struct {
}func (p *Pet) Speak(){ // Pet类的函数成员fmt.Print("Pet speak.\n")
}func (p *Pet) SpeakTo(host string) { // Pet类的函数成员p.Speak()fmt.Println("Pet SpeakTo ", host)
}// Dog 扩展Pet的功能
type Dog struct {p *Pet
}// 扩展父类的方法
func (d *Dog) Speak(){d.p.Speak()
}// 扩展父类的方法
func (d *Dog) SpeakTo(host string) {d.Speak()fmt.Println("Dog Speakto ", host)
}func TestDog(t *testing.T) {dog := new(Dog)dog.SpeakTo("Test dog")
}
以上测试代码的输出如下:
dog 的 SpeakTo 中调用了 dog 的 Speak,其中调用了 Pet 的 Speak,所以输出正常。
Pet 和 Dog 调用不会相互影响,完全由用户决定。
但是这和我们所想需要的不同,Pet 类有自己的方法,Dog 类有自己的方法,两者作用域完全不同。
这里Go语言推出了匿名嵌套类型,即 Dog 类不用实现自己的和 Pet 类同名的方法即可,通过在 Dog 类的声明中变更 Pet 成 员。
匿名嵌套类型
案例二: Go语言支持匿名函数类型
// Dog 扩展Pet的功能
type Dog struct {Pet
}
这样即不需要 Dog 声明自己的同名函数成员,默认的调用即为 Pet 成员函数的调用。
package oriented_testimport ("fmt""testing"
)type Pet struct {
}func (p *Pet) Speak(){fmt.Print("Pet speak.\n")
}func (p *Pet) SpeakTo(host string) {p.Speak()fmt.Println("Pet SpeakTo ", host)
}// Dog 扩展Pet的功能
type Dog struct {Pet // 支持匿名嵌套类型
}func TestDog(t *testing.T) {var dog Dogdog.Speak()dog.SpeakTo("Test dog")
}
最终的输出如下:
调用的都是 Pet 的成员函数,感觉像是继承了,因为继承默认就是子类能够使用父类的公有成员。
在匿名嵌套类型下,我们想要完整尝试一下Go语言是否真正支持继承,可以像之前的代码一样在 Dog 中实现 Pet 的同名函数,且能够通过父类对象调用子类的成员方法,像 C++/Java 这样进行向上类型转换(本身是不可能的,Go语言不支持显式类型转换)。
案例三: Go语言不支持继承,如下代码:
package oriented_testimport ("fmt""testing"
)type Pet struct {
}func (p *Pet) Speak(){fmt.Print("Pet speak.\n")
}func (p *Pet) SpeakTo(host string) {p.Speak()fmt.Println("Pet SpeakTo ", host)
}// Dog 扩展Pet的功能
type Dog struct {//p *PetPet // 支持匿名嵌套类型
}// 重载父类的方法
func (d *Dog) Speak(){fmt.Print("Dog speak.\n")
}// 重载父类的方法
func (d *Dog) SpeakTo(host string) {d.Speak()fmt.Println("Dog Speakto ", host)
}func TestDog(t *testing.T) {var dog Pet = new(Dog) // 这里就会编译错误dog.Speak()dog.SpeakTo("Test dog")
}
cannot use new(Dog) (value of type *Dog) as Pet value in variable declaration
不支持将Pet类型转换为Dog类型
总结一下,Go语言并不支持继承,能够支持接口的扩展 和 复用**(匿名嵌套类型)**,内嵌这种方式是完全不能当成继承来用的,因为它不支持访问子类的方法数据(重载),不支持LSP原则。
其中扩展 就是不同类实现相同的成员函数,能够实现类似于案例一中的扩展接口形态。
复用则是通过匿名嵌套类型实现 类似于重载的功能,可以看看案例二的代码。
多态
type Programmer interface {WriteHelloWorld() string
}type GoProgrammer struct {
}func (g *GoProgrammer) WriteHelloWorld() string {return "fmt.Println(\"Hello World\")"
}type JavaProgrammer struct {
}func (j *JavaProgrammer) WriteHelloWorld() string {return "System.out.println(\"Hello World\")"
}// 多态
func writeFirstProgram(p Programmer) {fmt.Printf("%T %v\n", p, p.WriteHelloWorld())
}func TestClient(t *testing.T) {goProgrammer := new(GoProgrammer)javaProgrammer := new(JavaProgrammer)writeFirstProgram(goProgrammer)writeFirstProgram(javaProgrammer)
}
空接口与断言
func DoSomething(p interface{}) {// '.' 断言,p.(int),p断言为int类型if i, ok := p.(int); ok {fmt.Println("Integer", i)return}if s, ok := p.(string); ok {fmt.Println("string", s)return}fmt.Println("UnKnow type")
}func TestEmptyInterface(t *testing.T) {DoSomething(10)DoSomething("10")DoSomething(10.00)
}
Go接口最佳实践
错误机制
error
package errorimport ("errors""testing"
)func GetFibonacci(n int) ([]int, error) {if n < 2 || n > 100 {return nil, errors.New("n should be in [2, 100]")}fibList := []int{1, 1}for i := 2; i < n; i++ {fibList = append(fibList, fibList[i-1]+fibList[i-2])}return fibList, nil
}func TestGetFibonacci(t *testing.T) {if v, err := GetFibonacci(-10); err != nil {t.Error(err)} else {t.Log(v)}
}
最佳实践
尽早失败,避免嵌套!
例:
func GetFibonacci2(str string) {var (i interr errorlist []int)if i, err = strconv.Atoi(str); err != nil {fmt.Println("Error", err)return}if list, err = GetFibonacci(i); err != nil {fmt.Println("Error", err)return}fmt.Println(list)
}
panic
panic vs os.Exit
revocer
recover
类似于 java 的 catch
。
func TestRecover(t *testing.T) {defer func() {if err := recover(); err != nil {fmt.Println("recovered from", err)}}()fmt.Println("Start")panic(errors.New("something wrong"))
}
其实上面这种修复方式是非常危险的。
我们一定要当心自己 revocer
在做的事,因为我们 revocer 并不检测到底发生了什么错误,而只是记录了一下或者直接忽略掉了,这时可能系统某些核心资源消耗完了,但我们把他强制恢复之后系统依然是不能正常工作的,还会导致我们的健康检查程序 health check
检查不出当前系统的问题,因为很多 health check 只是检查当前系统的进程在还是不在, 因为我们的进程是在的,所以就会形成僵尸进程
,它还活着,但它不能提供服务。
如果出现了这种问题,我们可以用 “Let is Crash”
可恢复的设计模式,我们直接 crash 掉,这样守护进程就会重新把服务进程提起来(说的有点高大上,其实就是重启),重启是恢复不确定性错误的最好方法。
包 package
构建可复用的模块(包)
my_series.go
package seriesfunc GetFibonacci(n int) ([]int, error) {ret := []int{1, 1}for i := 2; i < n; i++ {ret = append(ret, ret[i-1]+ret[i-2])}return ret, nil
}
package_test.go
引用另一个包中的方法:
package clientimport ("mygoland/geekvideo/ch13/series" // 包路径要从自己的gopath开始写起"testing"
)func TestPackage(t *testing.T) {t.Log(series.GetFibonacci(10))
}
init方法
func init() {fmt.Println("init1")
}func init() {fmt.Println("init2")
}func GetFibonacci(n int) []int {ret := []int{1, 1}for i := 2; i < n; i++ {ret = append(ret, ret[i-1]+ret[i-2])}return ret
}
如何使用远程的package
ConcurrentMap for GO
https://github.com/easierway/concurrent_map
使用 go get
命令导入
go get -u github.com/easierway/concurrent_map
package remoteimport (cm "github.com/easierway/concurrent_map" // 导入远程包"testing"
)func TestConcurrentMap(t *testing.T) {m := cm.CreateConcurrentMap(99)m.Set(cm.StrKey("key"), 10)t.Log(m.Get(cm.StrKey("key")))
}
依赖管理
Go未解决的依赖问题
vendor路径
常用的依赖管理工具
- godep:https://github.com/tools/godep
- glide:https://github.com/Masterminds/glide
- dep:https://github.com/golang/dep
安装glide
-
mac环境,使用 brew 安装 glide
brew install glide
安装成功
-
初始化 glide
glide init
-
glide init 执行完毕后,生成了一个
yaml
文件,并把依赖的包和版本号定义在了里面 -
在之前的目录下执行
glide install
然后就会在我们的指定的文件下面生成一个
vender
目录和glide.lock
文件。 -
到此为止,Go 就能 搜索到 vender 目录下面的 package 了,我们就通过 vender 来指定了包的路径和版本号,即实现了在同一环境下使用同一个包的不同版本依赖了。
笔记整理自极客时间视频教程:Go语言从入门到实战
相关文章:

【Go语言从入门到实战】面向对象编程篇
面向对象编程 Go语言的面向对象编程和其他语言有非常大的差别。 Go 是一种面向对象的语言吗? 是和不是。虽然 Go 有类型和方法,并允许面向对象的编程风格,但没有类型层次结构(继承)。Go 中的“接口”概念提供了一种不…...

代码随想录算法训练营第四十五天 | 力扣 70. 爬楼梯(进阶), 322. 零钱兑换, 279.完全平方数
70. 爬楼梯(进阶) 题目 70. 爬楼梯 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 改为:一步一个台阶,两个台阶,三个台阶ÿ…...

dvwa靶场通关(三)
第三关:CSRF(跨站请求伪造) csrf跨站请求伪造:是一种对网站的恶意利用。尽管听起来像跨站脚本,但它与xss非常不同,xss利用站点内受信任用户,而csrf则通过伪造来自受信任用户的请求来利用受信任…...
【计算机图形学】理论考核回顾
写在前面: 1:题型主要是单选题多选题判断题计算题,题目量居多,一定要合理安排时间。 2:小题由于太琐碎了,遂不回顾,大致都是课件上做过的小题,嗯。 3:后续有时间更新期…...

一文了解国内外电子后视镜(CMS)现行法规标准
摘要: 本文小编分享一篇整合了国内外对CMS的安装及功能性做出要求的相关标准与法规。感兴趣的朋友可以专门去搜索学习。 前言:随着GB15084-2022的即将正式实施,以摄像头屏幕组合取代传统光学后视镜的新一代电子后视镜CMS相关车型将被允许上路…...

LabVIEWCompactRIO 开发指南36 确定“Clock Ticks”或模拟时间
LabVIEWCompactRIO 开发指南36 确定“Clock Ticks”或模拟时间 桌面执行节点可以控制模拟时间,因此开发人员可以使用模拟I/O在开发计算机上执行期间更改关键点的激励。要成功使用此功能,需要测量FPGA VI完成所需的时间,或者需要以直观地知道…...

ESP32 :项目的创建及项目架构解析
一、项目的创建 方式一:基于IDF示例创建 在ESP-IDF中有example示例库,以其中的一个示例为模板创建项目。 1、打开示例库 查看 - 命令面板(也可以按住CtrlShiftP 或 F1) 输入 show examples projects 2…...
TI EDI 项目数据库方案开源介绍
TI EDI 工作流简介 TI EDI到SQL Server示例流具有预配置的端口,用于从TI的EDI集成规范转换以下交易集: 850 采购订单,企业 -> TI855 采购订单确认,TI -> 企业860 采购订单变更,企业 -> TI865 采购订单变更确认,TI -> 企业856 发货通知,TI …...

报表控件FastReport使用指南——使用NuGet包创建PDF文档
FastReport 是功能齐全的报表控件,可以帮助开发者可以快速并高效地为.NET,VCL,COM,ActiveX应用程序添加报表支持,由于其独特的编程原则,现在已经成为了Delphi平台最优秀的报表控件,支持将编程开…...

策略模式-类型统计
文章目录 前言一、策略模式是什么?二、策略模式应用场景三、策略模式优点四、策略模式缺点五、场景案例:类型统计1.项目结构2.UML图解3.代码实现3.1 指标枚举3.2 请求体3.3 响应体3.4.分析统计指标策略3.5.接口3.6.扩展接口3.7.接口实现3.8.控制层 六、P…...
android 12.0app应用安装白名单
1.概述 在12.0定制化开发中,客户需求要实现应用安装白名单功能,在白名单之中的应用可以安装,其他的app不准安装,实现一个 控制app安装的功能,这需要从app安装流程入手就可以实现功能 PMS就是负责管理app安装的,功能就添加在这里就可以了, 2.app应用安装白名单核心代码 …...
android 12.0Launcher3禁止拖动图标到Hotseat
1.概述 在12.0系统Launcher3进行定制化开发中,对于hotseat的开发中,由功能需求要求禁止拖动图标到Hotseat的功能,而拖拽也是在workspace.java中处理的 接下来就从workspace.java 开始找解决的办法 2.Launcher3禁止拖动图标到Hotseat相关代码分析 packages/apps/Launcher3…...
Java阶段三Day03
Java阶段三Day03 文章目录 Java阶段三Day03JavaScript语言JavaScript概述什么是JavaScript语言特点一门编程语言包括哪些内容 语言基础变量JavaScript常用数据类型运算符各种语句如何在HTML页面中引入JavaScript代码方法和页面相关的方法 BOM和DOM什么是BOMwindow对象location对…...

【STM32G431RBTx】备战蓝桥杯嵌入式→决赛试题→第七届
文章目录 前言一、题目二、模块初始化三、代码实现interrupt.h:interrupt.h:main.h:main.h: 四、完成效果五、总结 前言 无 一、题目 二、模块初始化 1.LCD这里不用配置,直接使用提供的资源包就行 2.ADC:开启ADCsingle-ended 3.LED:开启PC8-15,PD2输出模式就行了…...

Java日期时间调整的几种方式
一、Calendar类 我们现在已经能够格式化并创建一个日期对象了,但是我们如何才能设置和获取日期数据的特定部分呢,比如说小时,日,或者分钟? 我们又如何在日期的这些部分加上或者减去值呢? 答案是使用Calendar 类。 Calendar类的…...

如何在Windows 11更新后解决C盘已满的问题?
Windows 11比Windows 10需要占用C盘更多的空间,在升级到Windows 11后,如果升级后出现问题,安装程序可以帮你退回到Windows 10。无论怎样,在升级到Windows 11后,系统会自动制作以前的数据的副本,这会占用大量…...

DJ6-1/2/3 文件系统
目录 6.1 文件系统概述 6.1.1 文件、记录和数据项 6.1.2 文件类型 6.1.3 文件系统模型 6.1.4 对文件的操作 6.2 文件的逻辑结构 6.2.1 文件逻辑结构的类型 6.2.2 顺序文件(Sequential File) 6.2.4 索引文件(Index File&…...

华为OD机试真题 Java 实现【打印文件】【2023Q1 100分】
一、题目描述 有 5 台打印机打印文件,每台打印机有自己的待打印队列。 因为打印的文件内容有轻重缓急之分,所以队列中的文件有1~10不同的优先级,其中数字越大优先级越高。 打印机会从自己的待打印队列中选择优先级最高的文件来打印。 如果存在两个优先级一样的文件,则选…...

基于LSB实现文本、图片、压缩包的隐藏
关于LSB的相关介绍: LSB全称为 Least Significant Bit(最低有效位),是一种基于图片最低有效位修改储存信息的隐写方法,在CTF杂项中经常会遇到,LSB属于空域算法中的一种,是将信息嵌入到图像点中…...

(万字长文)Linux——IO之重定向+缓冲区 +重定向 +缓冲区原理实现 +带重定向的简易版shell+标准输出标准错误
索引 文件描述符分配规则重定向 缓冲区1.什么是缓冲区2.缓冲区在哪里 重定向源码模拟实现缓冲区原理带重定向的简易版Xshell标准输入和标准错误 文件描述符分配规则 文件描述符的分配规则 从头遍历数组fd_array[],找到一个最小的,没有被使用的下标,分配…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...

华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...

回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...