Go语言——没有对象的面向对象编程
本文译自Steve Francia在OSCON 2014的一个PPT,原作请前往:https://spf13.com/presentation/go-for-object-oriented-programmers/
对我来说,最吸引我的不是Go拥有的特征,而是那些被故意遗漏的特征。 —— txxxxd
为什么你要创造一种从理论上来说,并不令人兴奋的语言?
因为它非常有用。 —— Rob Pike
Go中的“对象”
要探讨Go语言中的对象,我们先搞清楚一个问题:
Go语言有对象吗?
从语法上来说,
- Go中没有类(Classes)
- Go中没有“对象”(Objects)
到底什么是对象?
对象是一种抽象的数据类型,拥有状态(数据)和行为(代码)。 —— Steve Francia
在Go语言中,我们这样声明一个类型:
类型声明(Struct)
type Rect struct {width intheight int
}
然后我们可以给这个Struct声明一个方法
func (r *Rect) Area() int {return r.width * r.height
}
用起来就像这样
func main() {r := Rect{width: 10, height: 5}fmt.Println("area: ", r.Area())
}
我们不光可以声明结构体类型,我们可以声明任何类型。比如一个切片:
类型声明(Slice)
type Rects []*Rect
同样也可以给这个类型声明一个方法
func (rs Rects) Area() int {var a intfor _, r := range rs {a += r.Area()}return a
}
用起来
func main() {r := &Rect{width: 10, height: 5}x := &Rect{width: 7, height: 10}rs := Rects{r, x}fmt.Println("r's area: ", r.Area())fmt.Println("x's area: ", x.Area())fmt.Println("total area: ", rs.Area())
}
https://play.golang.org/p/G1OWXPGvc3
我们甚至可以声明一个函数类型
类型声明(Func)
type Foo func() int
同样的,给这个(函数)类型声明一个方法
func (f Foo) Add(x int) int {return f() + x
}
然后用起来
func main() {var x Foox = func() int { return 1 }fmt.Println(x())fmt.Println(x.Add(3))
}
https://play.golang.org/p/YGrdCG3SlI
通过上边的例子,这样看来,其实
Go有“对象”
那么我们来看看
“面向对象”的Go
如果一种语言包含对象的基本功能:标识、属性和特性,则通常认为它是基于对象的。
如果一种语言是基于对象的,并且具有多态性和继承性,那么它被认为是面向对象的。 —— Wikipedia
第一条,我们在上边的例子看到了,go中的type declaration其实满足了Go语言是基于对象的。那么,
Go是基于对象的,它是面向对象的吗?
我们来看看关于第二条,继承性和多态性
继承
- 提供对象的复用
- 类是按层级创建的
- 继承允许一个类中的结构和方法向下传递这种层级
Go中实现继承的方式
- Go明确地避免了继承
- Go严格地遵循了符合继承原则的组合方式
- Go中通过嵌入类型来实现组合
组合
- 提供对象的复用
- 通过包含其他的对象来声明一个对象
- 组合使一个类中的结构和方法被拉进其他类中
继承把“知识”向下传递,组合把“知识”向上拉升 —— Steve Francia
嵌入类型
type Person struct {Name stringAddress
}type Address struct {Number stringStreet stringCity stringState stringZip string
}
给被嵌入的类型声明一个方法
func (a *Address) String() string {return a.Number + " " + a.Street + "\n" + a.City + ", " + a.State + " " + a.Zip + "\n"
}
使用组合字面量声明一个Struct
func main() {p := Person{Name: "Steve",Address: Address{Number: "13",Street: "Main",City: "Gotham",State: "NY",Zip: "01313",},}
}
跑起来试试
func main() {p := Person{Name: "Steve",Address: Address{Number: "13",Street: "Main",City: "Gotham",State: "NY",Zip: "01313",},}fmt.Println(p.String())
}
https://play.golang.org/p/9beVY9jNlW
升级
- 升级会检查一个内部类型是否能满足需要,并“升级”它
- 内嵌的数据域和方法会被“升级”
- 升级发生在运行时而不是声明时
- 被升级的方法被认为是符合接口的
升级不是重载
func (a *Address) String() string {return a.Number + " " + a.Street + "\n" + a.City + ", " + a.State + " " + a.Zip + "\n"
}func (p *Person) String() string {return p.Name + "\n" + p.Address.String()
}
外部结构的方法和内部结构的方法都是可见的
func main() {p := Person{Name: "Steve",Address: Address{Number: "13",Street: "Main",City: "Gotham",State: "NY",Zip: "01313",},}fmt.Println(p.String())fmt.Println(p.Address.String())
}
https://play.golang.org/p/Aui0nGa5Xi
这两个类型仍然是两个不同的类型
func isValidAddress(a Address) bool {return a.Street != ""
}func main() {p := Person{Name: "Steve",Address: Address{Number: "13",Street: "Main",City: "Gotham",State: "NY",Zip: "01313",},}// 这里不能用 p (Person类型) 作为 Address类型的IsValidAddress参数// cannot use p (type Person) as type Address in argument to isValidAddressfmt.Println(isValidAddress(p))fmt.Println(isValidAddress(p.Address))
}
https://play.golang.org/p/KYjXZxNBcQ
升级不是子类型
多态
为不同类型的实体提供单一接口
通常通过泛型、重载和/或子类型实现
Go中实现多态的方式
- Go明确避免了子类型和重载
- Go尚未提供泛型
- Go的接口提供了多态功能
接口
- 接口就是(要实现某种功能所需要提供的)方法的列表
- 结构上的类型 vs 名义上的类型
- “如果什么东西能做这件事,那么就可以在这使用它”
- 惯例上就叫它 某种东西
Go语言采用了鸭式辩型,和JavaScript类似。鸭式辩型的思想是,只要一个动物走起路来像鸭子,叫起来像鸭子,那么就认为它是一只鸭子。
也就是说,只要一个对象提供了和某个接口同样(在Go中就是相同签名)的方法,那么这个对象就可以当做这个接口来用。并不需要像Java中一样显式的实现(implements)这个接口。
接口声明
type Shaper interface{ Area() int
}
然后把这个接口作为一个参数类型
func Describe(s Shaper) {fmt.Println("Area is: ", s.Area())
}
这样用
func main() {r := &Rect{width: 10, height: 5}x := &Rect{width: 7, height: 10}rs := &Rects{r, x}Describe(r)Describe(x)Describe(rs)
}
https://play.golang.org/p/WL77LihUwi
“如果你可以重新做一次Java,你会改变什么?”
“我会去掉类class,” 他回答道。
在笑声消失后,他解释道,真正的问题不是类class本身,而是“实现”的继承(类之间extends的关系)。接口的继承(implements的关系)是更可取的方式。
只要有可能,你就应该尽可能避免“实现”的继承。
—— James Gosling(Java之父)
Go的接口是基于实现的,而不是基于声明的
这也就是上边所说的鸭式辩型
接口的力量
io.Reader
type Reader interface {Read(p []byte) (n int, err error)
}
- Interface
- Read方法读取最多len(p) bytes的数据到字节数组p中
- 返回读取的字节数和遇到的任何error
- 并不规定Read()方法如何实现
- 被诸如 os.File, bytes.Buffer, net.Conn, http.Request.Body等等使用
io.Writer
type Writer interface {Write(p []byte) (n int, err error)
}
- Interface
- Write方法写入最多len(p) bytes的数据到字节数组p中
- 返回写入的字节数和遇到的任何error
- 并不规定Write()方法如何实现
- 被诸如 os.File, bytes.Buffer, net.Conn, http.Request.Body等等使用
io.Reader 使用
func MarshalGzippedJSON(r io.Reader, v interface{}) error {raw, err := gzip.NewReader(r)if err != nil {return err}return json.NewDecoder(raw).Decode(&v)
}
读取一个json.gz文件
func main() {f, err := os.Open("myfile.json.gz")if err != nil {log.Fatalln(err)}defer f.Close()m := make(map[string]interface{})MarshalGzippedJSON(f, &m)
}
实用的交互性
- Gzip.NewReader(io.Reader) 只需要传入一个io.Reader接口类型即可
- 在files, http requests, byte buffers, network connections, ...任何你创建的东西里都能工作
- 在gzip包里不需要任何特殊处理。只要简单地调用Read(n),把抽象的部分留给实现者即可
将 http response 写入文件
func main() {resp, err := http.Get("...")if err != nil {log.Fatalln(err)}defer resp.Body.Close()out, err := os.Create("filename.ext")if err != nil {log.Fatalln(err)}defer out.Close()io.Copy(out, resp.Body) // out io.Writer, resp.Body io.Reader
}
Go
简单比复杂更难:你必须努力使你的思维清晰,使之简单。但最终还是值得的,因为一旦你到了那里,你就可以移山。 —— Steve Jobs
Go简单,实用,绝妙
Go做了一些伟大的事情
转载于:https://www.cnblogs.com/xxnn/p/10875693.html
Go语言——没有对象的面向对象编程相关推荐
- 基于对象和面向对象编程范式辨析和主流编程语言中的应用
基于对象和面向对象编程范式辨析和主流编程语言中的应用 前言 本文的目的是想告诉大家,为什么C++的模板这么强大.为什么Ruby的Duck Typing(像鸭子那样编程)这么强大! 基于对象和面向对象编 ...
- ruby 新建对象_Ruby面向对象编程的简介
ruby 新建对象 by Saul Costa 由Saul Costa Object-oriented programming (OOP) is a programming paradigm orga ...
- 允许使用抽象类类型 isearchboxinfo 的对象_Java面向对象编程三大特征 - 多态
Java面向对象编程三大特征 - 多态 本文关键字:Java.面向对象.三大特征.多态 多态是面向对象编程的三大特征之一,是面向对象思想的终极体现之一.在理解多态之前需要先掌握继承.重写.父类引用指向 ...
- ruby 新建对象_Ruby面向对象编程简介
ruby 新建对象 by Nishant Mishra 由Nishant Mishra Ruby面向对象编程简介 (An Introduction to Object-Oriented Program ...
- 通过两个案例,从C语言过渡到Java面向对象编程
⭐欢迎来到JAVA语法专栏,一起学习,一起进步⭐ 金九银十秋招季,也是开学的时候,不少同学开了JAVA这门课.一起来看看这两个案例来熟悉熟悉吧. 一.案例来源 案例源自Java基础案例教程一书,现为学 ...
- R语言基于R6的面向对象编程
R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大. R语言作为统计学一门语言,一直在小众领域闪耀着光芒.直到大数据的爆发,R语言变成了一门炙手可 ...
- [Python]小甲鱼Python视频第037课(类和对象:面向对象编程 )课后题及参考解答
# -*- coding: utf-8 -*- """ Created on Mon Mar 11 20:09:54 2019@author: Administrator ...
- Go语言基础之接口(面向对象编程下)
1 接口 搜书 198200.com 1.1 接口介绍 接口(interface)是Go语言中核心部分,Go语言提供面向接口编程,那么接口是什么? 现实生活中,有许多接口的例子,比如说电子设备上的充电 ...
- c语言中如何使用面向对象编程,如何使用C语言的面向对象
我们都知道,C++才是面向对象的语言,但是C语言是否能使用面向对象的功能? (1)继承性 typedef struct _parent { int data_parent; }Parent; type ...
- [C++程序语言设计笔记一]面向对象编程抽象,继承,重写基本介绍
今天是个不错的日子,不仅有人收了我做徒弟从此传授我有关C++的一些知识,由于前一段时间喜欢上了外挂的研究也用到了一些MFC的知识及一些Windows APIs编程,但是对C++还是没有从根本上认识.我 ...
最新文章
- 对IsUnderPostmaster变量初步学习
- 逆误差函数:torch.erfinv
- 操作系统:Android(Google公司开发的操作系统)
- 【机器学习基础】机器学习理论最佳入门!周志华宝箱书搭档,开源教程《钥匙书-KeyBook》发布...
- Codeforces Round #620 (Div. 2) E. 1-Trees and Queries 思维 + LCA
- php把中文写入mysql_php写入mysql中文乱码的实例解决方法
- 李彦宏开年内部信:2018营收破千亿,做出好产品的百度已归来
- 如何向 Linux Kernel 提交 Patch
- 09月23日 pytorch与resnet(一)
- Shiro设定密码匹配规则(自定义密码验证匹配器)
- 硬件PCB发热严重,天线信号不良的整改
- 移动硬盘使用时间、读写速度和坏道查询
- python ttk.notebook_python – 删除Ttk Notebook标签虚线
- UE4 初学者内容包介绍
- Chrome 书签你知道怎么导入吗(谷歌浏览器的书签保存在哪里 、谷歌浏览器书签保存在哪个文件夹)
- Excel打印不完整
- 微信小程序云开发实现一个社区 Demo(补充)
- r34300u和r53500u 哪个好
- 【XSY3367】青春野狼不做姐控偶像的梦(线段树)
- 卡卡IT学院模式:轻培训业态重线下资源整合
热门文章
- mongoDB 注册成开启自启动项
- 新三板专家程晓明:四板将是推动区块链技术与资本市场结合试验田
- 统计每个日期新用户的次日留存率
- unity中计算三角形的外接圆
- Pvr_UnitySDKAPI
- 富士康计划豪掷4000万美元投资美国制造业
- 反向传播算法为什么要“反向”
- Springboot with Impala and Kudu
- eclipse报错:The project was not built due to “Could not delete...”. Fix the problem...
- Reactjs开发基础