Go:好像面向对象了,但也没完全
本文最后更新于一年前或更久前,其中的信息可能已经有所发展或是发生改变。

何为面向对象

这里给出一个面向对象的定义:
面向对象系统将数据和代码通过“对象”集成到一起,而不是将程序看成由分离的数据和代码组成。对象是数据类型的抽象,它有状态(数据)和行为(代码)
面向对象包括继承、多态、虚派生等特性

Go语言本身就不是一个面向对象的编程语言,所以Go语言中没有类的概念,但是他是支持类型的,因此我们可以使用struct类型来提供类似于java中的类的服务,可以定义属性、方法、还能定义构造器。

type Hero struct {
    Name string
    Age uint64
}

func NewHero() *Hero {
    return &Hero{
        Name: "盖伦",
        Age: 18,
    }
}

func (h *Hero) GetName() string {
    return h.Name
}

func (h *Hero) GetAge() uint64 {
    return h.Age
}

func main()  {
    h := NewHero()
    print(h.GetName())
    print(h.GetAge())
}

可以见到,NewHero为一个用于构造Hero对象的构造函数,Go语言中无默认构造函数,构造函数必须自己创造,一般使用New+{对象(结构体)名}作为构造函数的名称。

封装

封装是把一个对象的属性私有化,同时提供一些可以被外界访问的属性和方法,如果不想被外界访问,我们大可不必提供方法给外界访问。在Go语言中实现封装我们可以采用两种方式:

  • Go语言支持包级别的封装,小写字母开头的名称只能在该包内程序中可见,所以我们如果不想暴露一些方法,可以通过这种方式私有包中的内容,这个理解比较简单,就不举例子了。
  • Go语言可以通过 type 关键字创建新的类型,所以我们为了不暴露一些属性和方法,可以采用创建一个新类型的方式,自己手写构造器的方式实现封装
type IdCard string

func NewIdCard(card string) IdCard {
    return IdCard(card)
}

func (i IdCard) GetPlaceOfBirth() string { //封装的方法
    return string(i[:6])
}

func (i IdCard) GetBirthDay() string { //封装的方法
    return string(i[6:14])
}

继承

Go没有继承,而是组合

go有意得被设计为没有继承语法。但这并不意味go中的对象(struct value)之间没有关系,只不过go的作者选择了另外一种机制来暗含这种特性:Go的设计理念为“组合优于继承”,这一机制也就是组合
Go语言严格遵守composition over inheritance principle的原则。go通过在struct和interface上使用组合和多态来实现继承关系。

Go并没有原生级别的继承支持,通过结构体内嵌类型的方式实现继承,典型的应用是内嵌匿名结构体类型和内嵌匿名接口类型,这两种方式还有点细微差别:

内嵌匿名结构体类型

说明

将父结构体嵌入到子结构体中,子结构体拥有父结构体的属性和方法,但是这种方式不能支持参数多态
参数多态的个人理解:对父结构体(对象)的方法的重写

例子

type Person struct {
   Name string
   Address Address
}

type Address struct {
   Number string
   Street string
   City   string
   State  string
   Zip    string
}

func (p *Person) Talk() {
    fmt.Println("Hi, my name is", p.Name)
}

func (p *Person) Location() {
    fmt.Println("I’m at", p.Address.Number, p.Address.Street, p.Address.City, p.Address.State, p.Address.Zip)
}

func main() {
    p := Person{
        Name: "Steve",
        Address: Address{
            Number: "13",
            Street: "Main",
            City:   "Gotham",
            State:  "NY",
            Zip:    "01313",
        },
    }

    p.Talk()
    p.Location()
}

Output

Hi, my name is Steve
I’m at 13 Main Gotham NY 01313

组合导致这样的对象关系实际上是in而不是is的关系

很多刚上手Go语言的小白使用了这样的方法后就发现无法修改父结构体(对象)的方法
这时候就需要用到内嵌匿名接口类型

内嵌匿名接口类型

说明

将接口类型嵌入到结构体中,该结构体默认实现了该接口的所有方法,该结构体也可以对这些方法进行重写,这种方式可以支持参数多态,这里要注意一个点是如果嵌入类型没有实现所有接口方法,会引起编译时未被发现的运行错误

例子

直接拿一个业务场景举例子,假设现在我们现在要给用户发一个通知,webapp端发送的通知内容都是一样的,但是点击后的动作是不一样的,所以我们可以进行抽象一个接口OrderChangeNotificationHandler来声明出三个公共方法:GenerateMessageGeneratePhotosgenerateUrl,所有类都会实现这三个方法,因为webapp端发送的内容是一样的,所以我们可以抽相出一个父类OrderChangeNotificationHandlerImpl来实现一个默认的方法,然后在写两个子类WebOrderChangeNotificationHandlerAppOrderChangeNotificationHandler去继承父类重写generateUrl方法即可,后面如果不同端的内容有做修改,直接重写父类方法就可以了

父结构体和结构定义:

type Photos struct {
    width uint64
    height uint64
    value string
}

type OrderChangeNotificationHandler interface {
    GenerateMessage() string
    GeneratePhotos() Photos
    generateUrl() string
}
type OrderChangeNotificationHandlerImpl struct {
    url string
}


父类型的构造方法和调用方法:

func NewOrderChangeNotificationHandlerImpl() OrderChangeNotificationHandler {//构造方法
    return OrderChangeNotificationHandlerImpl{
        url: "https://base.test.com",
    }
}
func (o OrderChangeNotificationHandlerImpl) GenerateMessage() string {
    return "OrderChangeNotificationHandlerImpl GenerateMessage"
}

func (o OrderChangeNotificationHandlerImpl) GeneratePhotos() Photos {
    return Photos{
        width: 1,
        height: 1,
        value: "https://www.baidu.com",
    }
}

func (w OrderChangeNotificationHandlerImpl) generateUrl() string {
    return w.url
}

子类型的定义和对父类型的方法的重写:

type WebOrderChangeNotificationHandler struct {
    OrderChangeNotificationHandler
    url string
}

func (w WebOrderChangeNotificationHandler) generateUrl() string {
    return w.url
}

type AppOrderChangeNotificationHandler struct {
    OrderChangeNotificationHandler
    url string
}

func (a AppOrderChangeNotificationHandler) generateUrl() string {
    return a.url
}

func check(handler OrderChangeNotificationHandler)  {
    fmt.Println(handler.GenerateMessage())
}

主函数进行测试:

func main()  {
    base := NewOrderChangeNotificationHandlerImpl()
    web := WebOrderChangeNotificationHandler{
        OrderChangeNotificationHandler: base,
        url: "http://web.test.com",
    }
    fmt.Println(web.GenerateMessage())
    fmt.Println(web.generateUrl())

    check(web)
}

因为所有组合都实现了OrderChangeNotificationHandler类型,所以可以处理任何特定类型以及是该特定类型的派生类的通配符。

多态

多态是面向对象编程的本质,多态是支代码可以根据类型的具体实现采取不同行为的能力,在Go语言中任何用户定义的类型都可以实现任何接口,所以通过不同实体类型对接口值方法的调用就是多态

type SendEmail interface {
    send()
}

func Send(s SendEmail)  {
    s.send()
}

type user struct {
    name string
    email string
}

func (u *user) send()  {
    fmt.Println(u.name + " email is " + u.email + "already send")
}

type admin struct {
    name string
    email string
}

func (a *admin) send()  {
    fmt.Println(a.name + " email is " + a.email + "will be sent to admin")
}

func main()  {
    u := &user{
        name: "asong",
        email: "你猜",
    }
    a := &admin{
        name: "asong1",
        email: "就不告诉你",
    }
    Send(u)
    Send(a)
}

References:

1️⃣ https://segmentfault.com/a/1190000040956053
2️⃣ https://segmentfault.com/a/1190000001832282
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇