1. 程式人生 > >go是面嚮物件語言嗎?

go是面嚮物件語言嗎?

前言

面向物件的含義引入了物件(object)、類(class)、繼承(inheritance)、子類(subclass)、虛方法(virtual method)、協程(coroutine)等概念。面向物件引入顛覆性的思想——將資料和邏輯完全分離。大部分程式設計師通過程式語言進行軟體開發都遵循著將資料和邏輯完全分離的原則。

由於面向物件沒有標準的定義,為了討論的方便,接下來我們將提供一個標準的定義。

面向物件系統將資料和程式碼通過“物件”整合到一起,而不是將程式看成由分離的資料和程式碼組成。物件是資料型別的抽象,它有狀態(資料)和行為(程式碼)

面向物件包括封裝、繼承、多型、虛派生等特性,接下來我們將看看go語言是怎樣處理物件、多型、繼承

,相信讀完接下來的介紹,您會對go是如何處理面向物件有自己的見解。

go中的物件-封裝

go語言中沒有物件(object)這個關鍵詞。物件(object)僅僅是一個單詞,重要的是它所表示的封裝資料含義。儘管go中沒有object這種型別,但是go中的struct有著跟object相同的特性。而在go中,採用了一種算是規定的方法:使用大小寫來確定,大寫是包外可見的,小寫的struct或函式只能在包內使用。

struct是一種包含了命名域和方法的型別

讓我們從一個例子中來理解它:

type rect struct {
    width int
    height int
}

func (r *rect) area() int {
    return r.width * r.height
}

func main() {
    r := rect{width: 10, height: 5}
    fmt.Println("area: ", r.area())
}

(1) 程式碼的第一塊定義了一個叫做rect的struct型別,該struct含有兩個int型別的域;

(2) 接下來定義了一個繫結在rect struct型別上的area方法。嚴格來說,area方法是繫結在指向rectct struct的指標上。如果方法繫結在rect type而非指標上,則在呼叫方法的時候需要使用該型別的值來呼叫,即使該值是空值,本例的空值實際是一個nil值;

(3) 程式碼的最後一塊是main函式,main函式第一行建立了一個rect型別的值,當然也有其他的方法來建立一個型別的值,這裡給出的是一個地道的方法。main函式的最後一行是列印作用在r值上的area方法的返回結果。

通過上面的描述,可以看出這很像物件的行為,我們可以建立一個結構化的資料型別,然後定義方法和這些資料進行互動

。上述的簡單例子並沒有完成展示面向物件的所有特性,比如繼承和多型。需要說明的是go不僅可以在struct上定義方法,在任何命名的型別上同樣也可以。比如,可以定義一個名為Counter的新型別,該型別是int型的別名,然後在Counter型別上定義方法。例子詳見:http://play.golang.org/p/LGB-2j707c

繼承和多型

定義物件間的關係的方法有如下幾種,它們之間都有一些差別,但目的都是一樣的:複用程式碼
單繼承(Inheritance) 多繼承(Multiple Inheritance) 多型(Subtyping/Polymorphism)物件組合(Object composition)
繼承:一個物件基於另外一個物件,使用其實現。有兩種不同的繼承實現:單繼承和多繼承。它們的不同在於物件是繼承自一個物件還是多個物件。單繼承關係是一棵樹,而多繼承關係是一個格狀結構。單繼承語言包括PHP、C#、Java、Ruby等,多繼承語言包括Perl、Python、C++等

多型

多型是is-a的關係,繼承是實現的複用。多型定義了兩個物件的語義關係,繼承定義兩個物件的語法關係。

物件組合

物件組合是一個物件包含了其他物件,而非繼承,它是has-a的關係,而非is-a。

go語言的繼承

go有意得被設計為沒有繼承語法。但這並不意味go中的物件(struct value)之間沒有關係,只不過go的作者選擇了另外一種機制來暗含這種特性。實際上go的這種設計是一種非常好的解決方法,它解決了圍繞著繼承的數十年的老問題和爭論。

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()
}

程式執行結果:


上面的例子需要注意的是, Address仍然是一個不同的物件,只不過存在於Person中

go中的偽多型

我們通過擴充套件上面的例子來說明go中的偽多型。注意這裡“偽”字說明實際上go是沒有多型的概念的,只不過偽多型表現得像多型一樣。下面的例子中,Person可以說話(Talk),一個Citizen也同時是一個Person,因此他也能說話(Talk)。在上面的例子中加入如下內容,完整程式碼見:http://play.golang.org/p/eCEpLkQPR3

type Citizen struct {
   Country string
   Person
}

func (c *Citizen) Nationality() {
    fmt.Println(c.Name, "is a citizen of", c.Country)
}

func main() {
    c := Citizen{}
    c.Name = "Steve"
    c.Country = "America"
    c.Talk()
    c.Nationality()
}

上面的例子通過引入匿名域(Person)實現了is-a關係。Person是Citizen的一個匿名域(anonymous field),匿名域只給出了物件型別,而不給出型別的名字。通過匿名域,Citizen可以訪問Person中的所有屬性(域)和方法。程式執行結果如下所示:


匿名域方法提升

上述例子,Citizen可以和Person執行一樣的Talk()方法。但如果想要Citizen的Talk()表現出不同的行為該怎麼做呢?我們只需要在Citizen上定義方法Talk()即可。當呼叫c.Talk()的時候,呼叫的則是Citizen的Talk()方法而非Person的Talk()方法,http://play.golang.org/p/jafbVPv5H9

func (c *Citizen) Talk() {
    fmt.Println("Hello, my name is", c.Name, "and I'm from", c.Country)
}

程式執行結果:


為何匿名域不是合適的多型實現

有兩個原因:

1. 匿名域仍然能被訪問,就好像它們是被嵌入的物件一樣。

這並不是一件壞事,多繼承存在的一個問題就是當多個父類具有相同的方法的時候,會產生歧義。然而go語言可以通過訪問跟匿名型別同名的屬性來訪問嵌入的匿名物件。實際上當使用匿名域的時候,go會建立一個跟匿名型別同名的物件。上面的例子中,修改main方法如下,我們能很清楚得看出這一點:

func main() {
//    c := Citizen{}
    c.Name = "Steve"
    c.Country = "America"
    c.Talk()         // <- Notice both are accessible
    c.Person.Talk()  // <- Notice both are accessible
    c.Nationality()
}
2、真正的多型,派生物件就是父物件
如果匿名物件能實現多型,則外層物件應該等同於嵌入的物件,而實際上並非如此,它們仍然是不同的存在。下面的例子印證了這一點:

package main

type A struct{
}

type B struct {
    A  //B is-a A
}

func save(A) {
    //do something
}

func main() {
    b := B
    save(&b);  //OOOPS! b IS NOT A
}

程式執行報錯:

prog.go:17: cannot use b (type *B) as type A in function argument
[process exited with non-zero status]

go中的真正的多型實現

正如我們上面提到的,多型是一種is-a的關係。在go語言中,每種型別(type)都是不同的,一種型別不能完全等同於另外一種型別,但它們可以繫結到同一個介面(interface)上。介面能用於函式(方法)的輸入輸出中,因而可以在型別之間建立起is-a的關係

go語言定義一個介面並不是使用using關鍵字,而是通過在物件上定義方法來實現。在Effective Go中指出,這種關係就像“如果某個東西能做這件事,那麼就把它應用到這裡”(不管黑貓白貓,只要能抓到老鼠,我就養這隻貓)。這一點很重要,因為這允許一個定義在package外的型別也能實現該介面。

我們接著上面的例子,增加一個新函式SpeakTo,然後修改main函式,將該方法應用到Citizen和Person上,http://play.golang.org/p/lvEjaMQ25D

func SpeakTo(p *Person) {
    p.Talk()
}

func main() {
    p := Person{Name: "Dave"}
    c := Citizen{Person: Person{Name: "Steve"}, Country: "America"}

    SpeakTo(&p)
    SpeakTo(&c)
}

程式執行結果出錯:型別不一致,因此可藉助 介面來實現。

Running it will result in
prog.go:48: cannot use c (type *Citizen) as type *Person in function argument
[process exited with non-zero status]

跟預期的結果一樣,編譯失敗。Citizen並不是Person型別,儘管他們擁有同樣的屬性。然而我們定義一個介面(interface)Human,然後將這個介面作為SpeakTo函式的輸入引數,上面的例子就可以正常運行了,http://play.golang.org/p/ifcP2mAOnf

type Human interface {
    Talk()
}

func SpeakTo(h Human) {
    h.Talk()
}

func main() {
    p := Person{Name: "Dave"}
    c := Citizen{Person: Person{Name: "Steve"}, Country: "America"}

    SpeakTo(&p)
    SpeakTo(&c)
}
程式輸出結果:
Hi, my name is Dave
Hi, my name is Steve

關於go語言中的多型,有如下兩點需要注意。

1. 可以把匿名域繫結到一個介面,也能繫結到多個介面。介面和匿名域一起使用,可以起到和多型同樣的效果。

2. go提供了多型的能力。介面的使用能使得實現了該介面方法的不同物件都能作為  函式的輸入引數  ,甚至作為 返回結果 ,但它們仍然保持了它們自己的型別。這點從上面的例子能看出來,我們不能直接在初始化Citizen物件的時候設定Name值,因為Name不是Citizen的屬性,而是Person的屬性,因而不能再初始化Citizen的時候設定Name值。

go,一個沒有object和inheritance的面向物件的語言

如上所述,面向物件的基本概念在go中被很好的實現了,雖然術語上存在差別。

(1) go把struct作為資料和邏輯的結合。

(2) 通過組合(composition),has-a關係來最小化程式碼重用,並且避免了繼承的缺陷。

(3) go使用介面(interface)來建立型別(type)之間的is-a關係。

通過上面的論述,Go語言通過其語法特性可以實現面向物件程式設計。歡迎進入無物件的OO程式設計模型世界!

討論

深入閱讀

相關推薦

go物件語言

前言面向物件的含義引入了物件(object)、類(class)、繼承(inheritance)、子類(subclass)、虛方法(virtual method)、協程(coroutine)等概念。面向物件引入顛覆性的思想——將資料和邏輯完全分離。大部分程式設計師通過程式語言進

go 學習筆記之go是不是物件語言是否支援面對物件程式設計?

面向物件程式設計風格深受廣大開發者喜歡,尤其是以 C++, Java 為典型代表的程式語言大行其道,十分流行! 有意思的是這兩中語言幾乎毫無意外都來源於 C 語言,卻不同於 C 的面向過程程式設計,這種面向物件的程式設計風格給開發者帶來了極大的便利性,解放了勞動,鬆耦合,高內聚也成為設計的標準,從而讓我們

Go語言入門系列】(八)Go語言是不是物件語言

[【Go語言入門系列】](https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&album_id=1441283546689404928)前面的文章: - [【Go語言入門系列】(五)指標和結構體的使用](https://mp.weixin.

物件語言的三大特徵

面嚮物件語言的三大特徵是:封裝 繼承 多型 最近感覺,總結一下這個問題還是挺有必要的,所以轉發了此篇文章的部分段落。   封裝   封裝是面向物件的特徵之一,是物件和類概念的主要特性。封裝,也就是把客觀事物封裝成抽象的類,並且類可以把自己的資料和方法只讓可信的

物件語言中寫純函式!

通常我們說函數語言程式設計時,提到的都是 lambda 表示式,也即函數語言程式設計中的“函式是頭等公民”的特點,然而函式式的另一個重要特點: 無副作用 ,在我看來更為重要。它可以在任何語言中實際應用。今天,我們來談一談面向物件中的“副作用”。 #什麼是副作用 In computer science, a

物件語言實現一個計算器控制檯程式

{63    cout<<"input A:";64    double numA;65    cin>>numA;    66    cout<<"input operator:";67    char oper;68    cin>>oper;69    c

Java到底是不是一種純物件語言

Java——是否確實的 “純面向物件”?讓我們深入到Java的世界,試圖來證實它。 在我剛開始學習 Java 的前面幾年,我從書本里知道了 Java 是遵循 “面向物件程式設計正規化(Object Oriented Programming paradigm)”的。在Ja

什麼是物件?什麼是面向物件程式設計?物件語言有什麼優點?

在初學面嚮物件語言的時候,很多書都會有這樣的句子–“一切都是物件”。那麼物件究竟是什麼呢?是不是一切的事物都叫物件?但這裡的物件並不是我們日常生活中的物件(事物),C#中我們把一個類的例項叫做物件,這裡的物件並非只是具體的事物,它也可以是一種規則、計劃或事件。在

物件語言和多型

什麼是多型呢? 字面意思就是同一事物有多種形態。 在面向物件程式設計中,多型指的是介面的多種不同的實現方式。程式設計其實就是一個將具體世界進行抽象化的過程,多型就是抽象化的一種體現,把一系列具體事物的共同點抽象出來, 再通過這個抽象的事物, 與不同的具體事物

漢語——世界上唯一的物件語言

宣告:除了漢語之外,我只會一點英語和日語。因此說是“唯一”略顯誇張,有吸引眼球的嫌疑,請大家原諒。嚴謹地說,標題應該是“漢語是一種面向物件的高階語言”。 在網上看到過很多有關漢語和英語比較的文章,他們寫的都很不錯,並且列舉了大量的事例來證明漢語或者英語是優秀的

GO是更好的程式語言

引言 團隊有專案考慮用GO重寫,所以花了些時間調研GO。 第一次接觸GO是2年前,17年3月份,全職鑽研一週,彼時C++中毒太深,內心排斥其他程式語言,看其他語法總覺得有點怪,而且有“C/C++能做任何事,故無用其他語言之必要”的思想在作祟。 人都有思維定勢,受限於自己的經驗和認知,我亦不能例外,但好

阿裏雲Ubuntu安裝圖形界與中文語言

nbsp oca detail sweet 語言 cat cati ubunt csdn 圖形界面: http://blog.csdn.net/qq_37608398/article/details/78155568?locationNum=9&fps=1

Go 語言和 Scala 語言對比

別人 計算機 tran tour linked 方式 對象 想要 content 我在Google寫過Go(自己的業余時間),也在LinkedIn寫過Scala。兩者都是具有一流的並發特性的現代語言。下面的回答是基於我編寫大規模的軟

Java程序員從阿裏、京東、美團面試回來,這些試題你會

Java 程序員 分布式 微服務 後端 最近有很多朋友去目前主流的大型互聯網公司面試(阿裏巴巴、京東、美團、滴滴),面試回來之後會發給我一些面試題。有些朋友輕松過關,拿到offer,但是有一些是來詢問我答案的。 其實本來真的沒打算寫這篇文章,主要是自己得記憶力不是很好,不像一些記憶力強的人

pycharm中Django在html文件裏使用模板語言

顏色 如何使用 使用 如何配置 for src text roc 有變 在pycharm的學習中,發現別的的django裏面額html寫的模板語言都是變顏色的,而且還能自動補全,而我們的pycharm的html文件卻是只能把自己的額模板語言自己手動的寫全,那是因為我們在使用

你在學習C語言?這些C語言專業書籍,你讀過幾本?

C語言誕生背景 1964 年,貝爾實驗室加入了通用電氣和麻省理工學院發起的計劃 MULTICS(一套安裝在大型主機上的分時多工作業系統)。由於專案進展緩慢,1969 年,貝爾實驗室宣佈退出。做 MULTICS 專案的時候,貝爾實驗室的工程師 Ken Thompson 寫了個遊戲 Space Tr

Go試題精編100題

成了 ... 展開 reflect pes vendor 路由 概念 序列 Golang精編100題 選擇題 1. 【初級】下面屬於關鍵字的是()A. funcB. defC. structD. class 參考答案:AC 2. 【初級】定義一個包內全局字

Java程式設計師從阿里拿到offer回來,這些試題你會

前不久剛從阿里面試回來,為了這場面試可以說準備了一個半月,做的準備就是刷題和看視訊看書充實自己的技術,話說是真難啊,不過還算順利拿到了offer,有很多面試題我已經記不起來了,這些是當天回家整理好的,下面我來跟大家一起分享一下。 首先我們需要明白一個事實,招聘的一個很關鍵的因素是在給自己找未來的同事,同

物件的反射和單列模式

a ="普通變數a"b ="普通變數b"A ="普通常量A"B ="普通常量B"import sysdef func(): print("func,function")def func1(): print("func1,function")while 1: res = input("<

Java程式設計師從阿里面試回來,這些試題你們會

前不久剛從阿里面試回來,為了這場面試可以說準備了一個半月,做的準備就是刷題和看視訊看書充實自己的技術,話說是真難啊,不過還算順利拿到了offer,有很多面試題我已經記不起來了,這些是當天回家整理好的,下面我來跟大家一起分享一下。 首先我們需要明白一個事實,招聘的一個很關鍵的因素是在給自己找未來的同事,同級別