Go基础语法(十二)

面向对象编程

Go 并不是完全面向对象的编程语言。

Go 官网的 FAQ 回答了 Go 是否是面向对象语言,摘录如下:

可以说是,也可以说不是。虽然 Go 有类型和方法,支持面向对象的编程风格,但却没有类型的层次结构。Go 中的“接口”概念提供了一种不同的方法,我们认为它易于使用,也更为普遍。Go 也可以将结构体嵌套使用,这与子类化(Subclassing)类似,但并不完全相同。此外,Go 提供的特性比 C++ 或 Java 更为通用:子类可以由任何类型的数据来定义,甚至是内建类型(如简单的“未装箱的”整型)。这在结构体(类)中没有受到限制。

使用结构体,而非类

Go 不支持类,而是提供了结构体。结构体中可以添加方法。这样可以将数据和操作数据的方法绑定在一起,实现与类相似的效果。

先来看一个例子,建立如下目录:


image.png

编辑people.go:

package demo

import "fmt"

type Boy struct {
    Name string
    Age int
    Sex string
}

func (b Boy)GetBoy() int{
    fmt.Println(b.Name, b.Age, b.Sex)
    return b.Age
}

一个结构体,挂了一个方法。
编辑main.go

package main

import "oop_demo/demo"

func main()  {
    b := demo.Boy{"allen", 20,"男"}
    b.GetBoy()
}

运行main.go。
以上就是可以用结构体实现面向对象的代码。

但是,这种方式如果在main中调用方法而未赋值的时候,会自动使用Boy结构体中变量的零值,如果在Java或Python这种OOP语言中,是使用构造器来解决这种问题,Go 并不支持构造器。如果某类型的零值不可用,需要程序员来隐藏该类型,避免从其他包直接访问。

使用New()函数,而非构造器

更改 people.go:

package demo

import "fmt"

type boy struct {
    name string
    age int
    sex string
}

func New(name string, age int, sex string) boy{
    b := boy {name, age, sex}
    return b
}

func (b boy)GetBoy() int{
    fmt.Println(b.name, b.age, b.sex)
    return b.age
}

更改main.go

package main

import "oop_demo/demo"

func main()  {
    b := demo.New("allen", 20,"男")
    b.GetBoy()
}

运行main.go查看结果与之前相同。

虽然 Go 不支持类,但结构体能够很好地取代类,而以 New(parameters) 签名的方法可以替代构造器。

组合取代继承

Go 不支持继承,但它支持组合(Composition)。组合一般定义为“合并在一起”。汽车就是一个关于组合的例子:一辆汽车由车轮、引擎和其他各种部件组合在一起。

1.通过嵌套结构体进行组合
一旦结构体内嵌套了一个结构体字段,Go 可以使我们访问其嵌套的字段,好像这些字段属于外部结构体一样。所以上面第 11 行的 p.author.fullName() 可以替换为 p.fullName()。于是,details() 方法可以重写,如下所示:

type post struct {  
    title     string
    content   string
    author
}

func (p post) details() {  
    fmt.Println("Title: ", p.title)
    fmt.Println("Content: ", p.content)
    fmt.Println("Author: ", p.author.fullName())
    fmt.Println("Bio: ", p.author.bio)
}


重写
func (p post) details() {  
    fmt.Println("Title: ", p.title)
    fmt.Println("Content: ", p.content)
    fmt.Println("Author: ", p.fullName())
    fmt.Println("Bio: ", p.bio)
}

2.结构体切片的嵌套

package main

import (  
    "fmt"
)

type author struct {  
    firstName string
    lastName  string
    bio       string
}

func (a author) fullName() string {  
    return fmt.Sprintf("%s %s", a.firstName, a.lastName)
}

type post struct {  
    title   string
    content string
    author
}

func (p post) details() {  
    fmt.Println("Title: ", p.title)
    fmt.Println("Content: ", p.content)
    fmt.Println("Author: ", p.fullName())
    fmt.Println("Bio: ", p.bio)
}

type website struct {  
 posts []post
}
func (w website) contents() {  
    fmt.Println("Contents of Website\n")
    for _, v := range w.posts {
        v.details()
        fmt.Println()
    }
}

func main() {  
    author1 := author{
        "Naveen",
        "Ramanathan",
        "Golang Enthusiast",
    }
    post1 := post{
        "Inheritance in Go",
        "Go supports composition instead of inheritance",
        author1,
    }
    post2 := post{
        "Struct instead of Classes in Go",
        "Go does not support classes but methods can be added to structs",
        author1,
    }
    post3 := post{
        "Concurrency",
        "Go is a concurrent language and not a parallel one",
        author1,
    }
    w := website{
        posts: []post{post1, post2, post3},
    }
    w.contents()
}

多态

Go 通过接口来实现多态。我们已经讨论过,在 Go 语言中,我们是隐式地实现接口。一个类型如果定义了接口所声明的全部方法,那它就实现了该接口。

用接口实现多态

关键代码:

type Income interface {
    calculate() int
    source() string
}

type FixedBilling struct {
    projectName string
    biddedAmount int
}

type TimeAndMaterial struct {
    projectName string
    noOfHours  int
    hourlyRate int
}

func (fb FixedBilling) calculate() int  {
    return fb.biddedAmount
}

func (fb FixedBilling) source() string {
    return fb.projectName
}

func (tm TimeAndMaterial) calculate() int{
    return tm.noOfHours * tm.hourlyRate
}

func (tm TimeAndMaterial) source() string {
    return tm.projectName
}

func calculateNetIncome(ic []Income)  {
    var count int = 0
    for _, v := range ic{
        fmt.Println(v.calculate())
        fmt.Println(v.source())
        count += v.calculate()
    }
    fmt.Println(count)
}

func main()  {
    pro1 := FixedBilling{"allen", 12000}
    pro2 := FixedBilling{"mary", 7000}
    pro3 := TimeAndMaterial{"tom", 12, 80}
    fmt.Println(pro1, pro2, pro3)
    ic := []Income{pro1, pro2, pro3}
    calculateNetIncome(ic)
}

上边代码中,FixedBilling 结构体和 TimeAndMaterial 都实现了接口中的两个方法,所以在calculateNetIncome函数中,可以通用通过v.calculate()调用不同结构体的calculate方法,这就是简单的多态。

参考 https://studygolang.com/articles/12681

Defer

defer 语句的用途是:含有 defer 语句的函数,会在该函数将要返回之前,调用另一个函数。
defer 不仅限于函数的调用,调用方法也是合法的。

示例:

package main

import (  
    "fmt"
)

func finished() {  
    fmt.Println("Finished finding largest")
}

func largest(nums []int) {  
    defer finished()
    fmt.Println("Started finding largest")
    max := nums[0]
    for _, v := range nums {
        if v > max {
            max = v
        }
    }
    fmt.Println("Largest number in", nums, "is", max)
}

func main() {  
    nums := []int{78, 109, 2, 563, 300}
    largest(nums)
}

输出:
Started finding largest  
Largest number in [78 109 2 563 300] is 563  
Finished finding largest

延迟方法

package main

import (  
    "fmt"
)


type person struct {  
    firstName string
    lastName string
}

func (p person) fullName() {  
    fmt.Printf("%s %s",p.firstName,p.lastName)
}

func main() {  
    p := person {
        firstName: "John",
        lastName: "Smith",
    }
    defer p.fullName()
    fmt.Printf("Welcome ")  
}

运行结果:
Welcome John Smith
延迟函数实参取值(Arguments Evaluation)

在 Go 语言中,并非在调用延迟函数的时候才确定实参,而是当执行 defer 语句的时候,就会对延迟函数的实参进行求值。

package main

import (  
    "fmt"
)

func printA(a int) {  
    fmt.Println("value of a in deferred function", a)
}
func main() {  
    a := 5
    defer printA(a)
    a = 10
    fmt.Println("value of a before deferred function call", a)

}

可以看到结果,a 变量值仍然为 5。
defer 栈

当一个函数内多次调用 defer 时,Go 会把 defer 调用放入到一个栈中,随后按照后进先出(Last In First Out, LIFO)的顺序执行。

func demo1()  {
    fmt.Println("allen")
}

func demo2()  {
    fmt.Println("is")
}

func demo3()  {
    fmt.Println("student")
}

func main()  {
    defer demo3()
    defer demo2()
    defer demo1()
    fmt.Println("end")
}

输出结果:
end
allen
is
student

再比如:字符串逆序输出

func main() {
    name := "Naveen"
    fmt.Printf("Orignal String: %s\n",  name)
    fmt.Printf("Reversed String: ")
    for _, v := range []rune(name) {
        defer fmt.Printf("%c", v)
    }
}
defer 的实际应用
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352

推荐阅读更多精彩内容

  • 1.安装 https://studygolang.com/dl 2.使用vscode编辑器安装go插件 3.go语...
    go含羞草阅读 1,553评论 0 6
  • Go的优点高效垃圾回收机制类型安全和内存安全(没有隐式转换,只能显示转换)快速编译(未使用包检测)轻松实现高并发支...
    Jarily阅读 502评论 0 0
  • 阳光短了 西山的晚霞早早的融入了夜色, 秋风起了 林荫小道上面飞满了先行的舞者 时光是条奔流不息的河 八月是道转折...
    西弗阅读 401评论 0 1
  • 我一直都坚定认为,公司饭堂的水果人气王当属小番茄。 连我这种极其嫌麻烦的人,每当有小番茄时都会心花怒放地多抓几颗进...
    瞬间收集阅读 253评论 0 0
  • 今天是女儿上小学的第二个周末,我们没有回老家,早上起床以后我们简单收拾了一下,就出门了,说好了回来做练习题。回...
    韩家琪妈妈阅读 221评论 0 0