Go中的继承与组合 | 青训营笔记

94 阅读3分钟

0 前言

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天

众所周知,golang不是面向对象的语言,所以很多其他面向对象的语言中的写法在go中不一定有类似的写法,但是go中又提供了一些语法糖,可以让我们模拟出部分面向对象的写法,做到用结构体来实现类的效果。

1 Go中的组合

go中的组合与其他语言中的组合其实没有太大的区别,就是将另一个结构体作为目标结构体的一个属性去使用,组合的写法如下:

type RegisteredClaims struct {
    Issuer string `json:"iss,omitempty"`
    Subject string `json:"sub,omitempty"`
    Audience ClaimStrings `json:"aud,omitempty"`
    ExpiresAt *NumericDate `json:"exp,omitempty"`
    NotBefore *NumericDate `json:"nbf,omitempty"`
    IssuedAt *NumericDate `json:"iat,omitempty"`
    ID string `json:"jti,omitempty"`
}
type MyCustomClaims struct {
    Username string `json:"username"`
    Password string `json:"password"`
    MyRegis jwt.RegisteredClaims
}

而我们可以通过类似类的引用成员变量的方式通过 . 的方式去引用组合的结构体,例如:

var test MyCustomClaimsTest
test.MyRegis.ID = "helloworld"

2 Go中的“继承”

严格来说Go是没有继承的,但是Go的语法糖可以让我们通过内嵌匿名结构体的方式去近似的做到类似继承的效果:

type MyCustomClaims struct {
    Username string `json:"username"`
    Password string `json:"password"`
    jwt.RegisteredClaims
}

上面的结构体与组合中的看起来很像,但实际上第三个属性仅仅包含了内嵌结构体的类型,而没有给它一个名称,因而其访问方式也有所不同。
值得注意的是:Go中的所有匿名属性其实都会有一个默认的名称,即属性名称,因而每种类型的匿名属性在一个结构体内的同一层次只能存在一个
例如上面的继承中的例子,我们可以这样进行访问:

claims := MyCustomClaims{
    Username: "zhanglei",
    Password: "douyin",
}
claims.RegisteredClaims = jwt.RegisteredClaims{
    NotBefore: jwt.NewNumericDate(time.Now()),
    ExpiresAt: jwt.NewNumericDate(time.Now()),
    Issuer:    "dy"}
claims.ID = "helloword"
claims.RegisteredClaims.ID = "wonderful"

后面两种访问ID的方式实际上是一样的,通过匿名属性的属性名和直接通过点进行访问都是访问到同一个属性
注:如果结构体中和其嵌套的结构体中有同名的属性,则直接通过 . 进行访问则会按照层级寻找嵌套关系最浅的那个属性,如果想访问层次更深的属性可以通过属性名称进行限定,这点与其他语言的类的继承有一些相似

3 Go中的成员函数

Go中的函数可以在函数名称前面加上一个接受者进行限定,接着我们便可以像使用类的成员函数一样去使用它,如:

func (c RegisteredClaims) Valid() error {}
//也可以写成
func (c *RegisteredClaims) Valid() error {}

var claims RegisteredClaims
claims.Valid()

注意根据接受者的类型是指针还是结构体,会有值传递和引用传递的不同,感兴趣的可以去了解下Go中的两种传参方式
Tips:这部分我感觉跟c++有一些类似,像接受者的写法很容易让我联想到c++中的 RegisteredClaims:: 的写法