,,繼承的特性子類Go-?Go

2023-11-26    分類: 網(wǎng)站建設(shè)

面向?qū)ο蟮木幊田L(fēng)格在開發(fā)者中非常流行,尤其以C++和Java為代表的編程語言風(fēng)靡一時(shí)!

有趣的是,這兩種語言幾乎不出所料都是從C語言衍生而來,但是它們不同于C的面向過程編程。這種面向?qū)ο蟮木幊田L(fēng)格給開發(fā)者帶來了極大的方便,解放了勞動(dòng),松耦合,高內(nèi)聚也成為了設(shè)計(jì)標(biāo)準(zhǔn),讓我們可以更愉快的復(fù)制粘貼,成為代碼的搬運(yùn)工。很多第三方工具都是開箱即用的,語義清晰,職責(zé)明確,都是面向?qū)ο蟮?。編程的好處?/p>

Go 語言也是從 C 語言派生而來的。不知道大家是否也好奇Go語言是否支持面向?qū)ο蟮木幊田L(fēng)格?

準(zhǔn)確地說,Go 支持面向?qū)ο缶幊?,而不是面向?qū)ο蟮恼Z言!

不,它和薛定諤的貓一樣不確定嗎?

其實(shí)這個(gè)答案是官方的答案,不是我自己憑空捏造出來的。詳情請(qǐng)參考Is Go an-?

為什么這么說?

Go 支持封裝,但不支持繼承和多態(tài),所以嚴(yán)格按照面向?qū)ο笠?guī)范,Go 語言不是面向?qū)ο蟮木幊陶Z言。

然而,Go 提供的接口是一種非常易于處理且更通用的方式。雖然它在表達(dá)上與其他主流編程語言略有不同,甚至無法實(shí)現(xiàn)多態(tài),但 Go 的接口不僅適用于結(jié)構(gòu)。 body,它也可以應(yīng)用于任何數(shù)據(jù)類型,這無疑是非常靈活的!

比較有爭(zhēng)議的是繼承。由于沒有關(guān)鍵字支持繼承特性,所以沒有繼承的痕跡。雖然有一些方法可以將類型嵌入到其他類型中來實(shí)現(xiàn)子類化,但這并不是真正的繼承。

因此,Go 既支持面向?qū)ο蟮木幊田L(fēng)格,又不完全是面向?qū)ο蟮木幊陶Z言。

如果換個(gè)角度看問題,正是因?yàn)闆]有繼承,Go比面向?qū)ο蟮木幊陶Z言更輕量級(jí)。您可能希望考慮繼承特性,子類和父類之間的關(guān)系,單繼承或多繼承。訪問控制權(quán)限等問題!

按照面向?qū)ο蟮木幊桃?guī)范,實(shí)現(xiàn)封裝特性的部分應(yīng)該是類和對(duì)象,但是這個(gè)概念和實(shí)現(xiàn)語言的關(guān)鍵詞是分不開的,但是Go沒有關(guān)鍵詞而是C語言 關(guān)鍵字,所以調(diào)用類或者對(duì)象不是很合適,所以下面的解釋過程還是采用這種結(jié)構(gòu)!

如何定義結(jié)構(gòu)

關(guān)鍵字聲明結(jié)構(gòu),屬性之間的回車和換行。

例如下面例子中定義了動(dòng)態(tài)數(shù)組結(jié)構(gòu),下面例子中將使用動(dòng)態(tài)數(shù)組結(jié)構(gòu)作為演示對(duì)象。

type MyDynamicArray struct {
	ptr *[]int
	len int
	cap int
}

在Go語言中定義對(duì)象的多個(gè)屬性時(shí),用直接換行代替分號(hào)來分隔?為什么它與其他主流編程語言不同?

對(duì)于習(xí)慣了分號(hào)結(jié)尾的開發(fā)者來說,他們可能有一段時(shí)間不習(xí)慣 Go 的這種語法,所以他們決定探索 Go 編程規(guī)范!

如果手動(dòng)添加分號(hào),編輯器會(huì)提示分號(hào)重復(fù),所以我猜可能是Go編譯器自動(dòng)添加了分號(hào),用分號(hào)作為語句語句的分隔符。手動(dòng)添加分號(hào)后,Go忽略了或者添加了分號(hào),所以報(bào)了上面的錯(cuò)誤。

這樣做有什么好處?

不是自己加分號(hào),編譯器無條件加分號(hào)的結(jié)果,更何況其他主流編程語言都是手動(dòng)加分號(hào)的!

當(dāng)有多個(gè)屬性時(shí),可以直接換行,不用加分號(hào)作為分隔符。對(duì)于從來沒有接觸過編程語言的小白來說,可能會(huì)省事,但是對(duì)于有編程經(jīng)驗(yàn)的開發(fā)者來說,要記住不能加分號(hào),真的很吵!

如果多個(gè)屬性寫在一行,則沒有換行符。我看你怎么區(qū)分它們。這個(gè)時(shí)候應(yīng)該用逗號(hào)還是分號(hào)隔開?

首先,空格不能分隔多個(gè)屬性,所以試試分號(hào)或逗號(hào)。

根據(jù)提示提示需要分號(hào)或換行符,換行符是標(biāo)準(zhǔn)形式,試試分號(hào)能不能分開?

此時(shí)編輯器不會(huì)報(bào)錯(cuò)或警告,所以一行上的多個(gè)屬性應(yīng)該用分號(hào)隔開,這意味著Go編譯器識(shí)別多個(gè)屬性還是和其他主流編程語言一樣。用數(shù)字隔開,但開發(fā)者不能用!

和上面的規(guī)則類似,記憶很簡(jiǎn)單,驗(yàn)證也比較容易。難點(diǎn)在于理解為什么?

為什么 Go 是這樣設(shè)計(jì)的?或者如何理解這種設(shè)計(jì)思想所代表的語義?

Go 作為一種新的編程語言,不僅體現(xiàn)在具體的語法差異上,更重要的是編程思想的特殊性。

就像面向?qū)ο笾械慕涌诟拍钜粯?,設(shè)計(jì)者只需要定義抽象的行為,并不關(guān)心行為的具體實(shí)現(xiàn)。

如果我們也用這種思維去理解不同的編程語言,那么就可以通過現(xiàn)象看本質(zhì),否則真的很容易陷入語法細(xì)節(jié),進(jìn)而可能會(huì)忽略背后的核心思想。

其實(shí)對(duì)于結(jié)構(gòu)的多屬性分隔符,其實(shí)不管用什么作為分隔符,逗號(hào)或者句號(hào)都可以,只要編譯器能識(shí)別出這是一個(gè)不同的屬性。

因?yàn)榇蠖鄶?shù)主流編程語言一般都是用分號(hào)作為分隔符,開發(fā)者需要手動(dòng)寫分隔符讓編譯器識(shí)別,但是Go語言不這么認(rèn)為,算了,直接換行,我也能識(shí)別出來out(雖然底層的 Go 編譯器在編譯時(shí)仍然使用分號(hào)來表示換行)!

加不加分號(hào),對(duì)于開發(fā)者來說,只是一個(gè)分隔多個(gè)屬性的標(biāo)志。如果不加就可以實(shí)現(xiàn),為什么還要加?

這三個(gè)基本問題是什么、為什么和如何。如果簡(jiǎn)單易學(xué)、易懂,學(xué)什么、怎么學(xué)就夠了,但這樣學(xué)、學(xué)就難免會(huì)出現(xiàn)自治的局面。也就是說,各種編程語言之間沒有關(guān)系,每種語言都是獨(dú)立的?!

世界上有千萬種語言,編程語言也有很多。學(xué)一門新語言不使用舊語言,學(xué)一門新語言和春小白有什么區(qū)別?

學(xué)習(xí)就是學(xué)習(xí),可惜對(duì)舊語言沒有幫助,也沒有加深對(duì)舊語言的理解。這只是對(duì)一種全新語言的純粹學(xué)習(xí)。

語言是由進(jìn)化創(chuàng)造的。它不是空中樓閣。它建立在現(xiàn)有系統(tǒng)的基礎(chǔ)上,逐步發(fā)展和演進(jìn)。任何新語言或多或少都會(huì)找到舊語言的影子。

那何不嘗試一下,弄清楚新語言設(shè)計(jì)的初衷和設(shè)計(jì)過程中面臨的問題,然后再看語言是如何解決問題的。求解的過程稱為實(shí)現(xiàn)細(xì)節(jié)。我覺得這種方式應(yīng)該是更好的學(xué)習(xí)方式!

雖然你不能在語言設(shè)計(jì)環(huán)境中,也不一定了解語言設(shè)計(jì)面臨的挑戰(zhàn),但先問并試著問為什么,你能不能不這樣設(shè)計(jì)等等,應(yīng)該是一個(gè)好的開始。

所以接下來的文章將采用語義分析的角度,嘗試?yán)斫釭o語言背后的原始設(shè)計(jì),并通過大量的輔助測(cè)試來驗(yàn)證猜想。不再是簡(jiǎn)單的知識(shí)羅列過程,當(dāng)然必要的知識(shí)歸納還是很重要的,這個(gè)自然不會(huì)放棄。

既然已經(jīng)定義了動(dòng)態(tài)數(shù)組,也就是設(shè)計(jì)者的工作暫時(shí)告一段落了。作為用戶,我們?nèi)绾问褂梦覀兊膭?dòng)態(tài)數(shù)組?

根據(jù)面向?qū)ο蟮男g(shù)語,從類創(chuàng)建對(duì)象的過程稱為實(shí)例化。但是,我們已經(jīng)知道 Go 并不是一個(gè)完整的面向?qū)ο笳Z言,所以為了避免使用面向?qū)ο蟮募夹g(shù)術(shù)語盡可能多地引用 Go 的實(shí)現(xiàn)細(xì)節(jié),我們可以暫時(shí)將其理解為結(jié)構(gòu)類型和結(jié)構(gòu)變量。隨著以后學(xué)習(xí)的深入,我們可能會(huì)對(duì)這部分有更深入的了解。

func TestMyDynamicArray(t *testing.T){
	var arr MyDynamicArray
	// { 0 0}
	t.Log(arr)
}

以上寫法沒有特別強(qiáng)調(diào)。它完全是使用之前文章中介紹過的語法規(guī)則來實(shí)現(xiàn)的。 var arr 表示聲明類型的變量 arr。此時(shí)直接打印變量的值,結(jié)果為{0 0}。

最后兩個(gè)值

都是0,自然容易理解,因?yàn)槲覀冊(cè)贕o語言中解釋變量的時(shí)候已經(jīng)介紹過了。 Go的變量類型默認(rèn)初始化有一個(gè)對(duì)應(yīng)的0值,而int類型的len cap屬性自然是0,而ptr *[]int是數(shù)組的指針,所以是nil。

等等,有些不對(duì)勁。這里有一個(gè)設(shè)計(jì)錯(cuò)誤。明明叫做動(dòng)態(tài)數(shù)組,里面的結(jié)果是切片的。怎么回事?

先修復(fù)這個(gè)錯(cuò)誤。可以看出,粗心大意的效果太糟糕了,語義發(fā)生了變化。我先糾正一下!

我們知道要使用數(shù)組,必須指定數(shù)組的初始化長(zhǎng)度。第一感覺是用cap所代表的容量來初始化*[cap]int數(shù)組,但是不行。編輯器提示必須使用整數(shù)。

雖然 cap 是一個(gè) int 類型的變量,但內(nèi)部數(shù)組 [cap]int 不識(shí)別這個(gè)方法。可能是因?yàn)檫@兩個(gè)變量是一起聲明的。 cap 和 (cap)int 都是變量,不能賦值。

那么如果指定了初始化長(zhǎng)度,應(yīng)該指定多少,如果是0,語義上是正確但與實(shí)際使用不符,因?yàn)檫@樣的話,內(nèi)部數(shù)組就不能按照方法插入了!

所以數(shù)組的初始化長(zhǎng)度不能為零,解決了無法操作數(shù)組的問題,但是語義不正確。因此,在這種情況下,需要維護(hù)len和cap這兩個(gè)變量的值,以確保語義和邏輯正確。 ,其中l(wèi)en代表數(shù)組的實(shí)際數(shù)量,cap代表內(nèi)部數(shù)組的實(shí)際分配長(zhǎng)度。由于這兩個(gè)變量非常重要,不應(yīng)被調(diào)用者隨意修改,最多只能查看變量的值,所以必須提供一種機(jī)制來保護(hù)變量的值。

接下來我們嘗試用函數(shù)封裝的思路來完成這個(gè)需求,代碼實(shí)現(xiàn)如下:

type MyDynamicArray struct {
	ptr *[10]int
	len int
	cap int
}
func TestMyDynamicArray(t *testing.T){
	var myDynamicArray MyDynamicArray
	t.Log(myDynamicArray)
	myDynamicArray.len = 0
	myDynamicArray.cap = 10
	var arr [10]int
	myDynamicArray.ptr = &arr
	t.Log(myDynamicArray)
	t.Log(*myDynamicArray.ptr)
}

var聲明結(jié)構(gòu)體變量并設(shè)置結(jié)構(gòu)體的基本屬性,然后操作內(nèi)部數(shù)組實(shí)現(xiàn)對(duì)數(shù)組的訪問修改。

然而,我們犯了一個(gè)典型的錯(cuò)誤。調(diào)用者不應(yīng)該關(guān)注實(shí)現(xiàn)細(xì)節(jié)。這不是打包要做的!

具體的實(shí)現(xiàn)細(xì)節(jié)由設(shè)計(jì)者完成面向?qū)ο缶幊陶Z言,并將相關(guān)數(shù)據(jù)封裝成一個(gè)整體,對(duì)外提供相應(yīng)的接口,讓調(diào)用者可以安全方便地調(diào)用。

第一步是封裝內(nèi)部數(shù)組相關(guān)的兩個(gè)變量,只對(duì)外提供訪問接口,不提供設(shè)置接口,防止調(diào)用者隨意修改。

顯然這部分應(yīng)該由函數(shù)來實(shí)現(xiàn),所以有如下轉(zhuǎn)換過程。

可惜編輯器直接報(bào)錯(cuò):它必須是類型名或指向類型名的指針。

函數(shù)不能放置在結(jié)構(gòu)中。這與 C 系列非常相似,但是像 Java 這樣的衍生系列會(huì)覺得不可思議。無論如何,這意味著結(jié)構(gòu)只能定義結(jié)構(gòu)而不能定義行為!

那我們把函數(shù)移到結(jié)構(gòu)外,但是我們定義的函數(shù)名字叫l(wèi)en,而且系統(tǒng)也有l(wèi)en函數(shù),這時(shí)候能正常運(yùn)行嗎?讓我們拭目以待,眼見為實(shí)。

除了函數(shù)本身報(bào)錯(cuò),函數(shù)內(nèi)部的len也報(bào)錯(cuò),因?yàn)榇藭r(shí)函數(shù)和結(jié)構(gòu)體還沒有建立任何連接。如何訪問 len 屬性?不報(bào)錯(cuò)才怪!

解決這個(gè)問題很簡(jiǎn)單。直接將結(jié)構(gòu)體的指針傳遞給len函數(shù)是不夠的,這樣在函數(shù)內(nèi)部可以訪問結(jié)構(gòu)體的屬性。

從設(shè)計(jì)的角度來看,它確實(shí)解決了函數(shù)定義的問題,但是用戶調(diào)用函數(shù)的方式看起來與面向?qū)ο蟮木帉懛绞接行┎煌?/p>

func TestMyDynamicArray(t *testing.T) {
	var myDynamicArray MyDynamicArray
	t.Log(myDynamicArray)
	myDynamicArray.len = 0
	myDynamicArray.cap = 10
	var arr [10]int
	myDynamicArray.ptr = &arr
	t.Log(myDynamicArray)
	t.Log(*myDynamicArray.ptr)
	(*myDynamicArray.ptr)[0] = 1
	t.Log(*myDynamicArray.ptr)
	t.Log(len(&myDynamicArray))
}

面向?qū)ο蟮姆椒ㄒ话愣际峭ㄟ^點(diǎn)操作符來實(shí)現(xiàn)的。訪問屬性或方法,以及我們實(shí)現(xiàn)的屬性訪問。但是方法是函數(shù)調(diào)用的典型形式嗎?這看起來不像是一種方法!

為了讓普通函數(shù)看起來像面向?qū)ο蟮姆椒?,Go做了如下改動(dòng),將當(dāng)前結(jié)構(gòu)體的變量聲明移到函數(shù)名的前面,從而實(shí)現(xiàn)類似于this或self in 面向語言的效果。

func len(myArr *MyDynamicArray) int {
	return myArr.len
}

這時(shí)候方法名和參數(shù)返回值又報(bào)錯(cuò)了。根據(jù)提示,函數(shù)名和字段名不能相同?

這真的是一件很神奇的事情。有沒有可能 Go 無法區(qū)分函數(shù)和字段?這是未知的。

然后我們要修改函數(shù)名,改成面向?qū)ο笾辛餍械姆椒?guī)則,如下:

func (myArr *MyDynamicArray) GetLen() int {
	return myArr.len
}

讓我們簡(jiǎn)單地談?wù)?Go 的可訪問性規(guī)則。大寫字母開頭表示公共權(quán)限,小寫字母開頭表示私有權(quán)限。 Go 只有這兩種類型的權(quán)限,這兩種權(quán)限都是特定于包的。先這樣理解就好了。

根據(jù)實(shí)驗(yàn)中得到的方法規(guī)則,繼續(xù)改進(jìn)其他方法,補(bǔ)充其他方法。

現(xiàn)在我們已經(jīng)解決了私有變量的可訪問性問題。初始化邏輯沒有處理。一般來說,初始化邏輯可以在構(gòu)造函數(shù)中執(zhí)行。 Go 是否支持構(gòu)造函數(shù)以及如何觸發(fā)構(gòu)造函數(shù)?功能?

嘗試按照其他主流編程語言中構(gòu)造函數(shù)的編寫方式來編寫Go的構(gòu)造函數(shù)。沒想到Go編譯器直接報(bào)錯(cuò),提示重新定義了類型,影響了其余部分!

如果修改方法名,理論上可以解決報(bào)錯(cuò)問題,但這不是構(gòu)造函數(shù)的樣子。 Go 可能不支持構(gòu)造函數(shù)嗎?

此時(shí)構(gòu)造函數(shù)的面向?qū)ο笮问睫D(zhuǎn)化為自定義函數(shù)實(shí)現(xiàn)的構(gòu)造函數(shù)。更準(zhǔn)確的說,這是一個(gè)類似于工廠模式實(shí)現(xiàn)的構(gòu)造函數(shù)方法。

func NewMyDynamicArray() *MyDynamicArray {
	var myDynamicArray MyDynamicArray
	return &myDynamicArray
}

Go 語言真的不支持構(gòu)造函數(shù)嗎?

至于是否支持構(gòu)造函數(shù),或者應(yīng)該如何支持,真相不明。隨著學(xué)習(xí)的深入,相信以后會(huì)有明確的答案。以下是我個(gè)人觀點(diǎn)的簡(jiǎn)要表達(dá)。

首先我們知道Go的結(jié)構(gòu)體只能定義數(shù)據(jù),結(jié)構(gòu)體的方法必須定義在結(jié)構(gòu)體之外。為了符合面向?qū)ο蟮氖褂昧?xí)慣,即通過實(shí)例對(duì)象的點(diǎn)操作符來訪問方法。 Go的方法只能是函數(shù)的變體,即普通函數(shù)指向結(jié)構(gòu)體變量的聲明部分,移到函數(shù)名前面來實(shí)現(xiàn)方法。這種把函數(shù)變成方法的模式也符合Go一貫的命名規(guī)則:按照人的思維習(xí)慣命名,先有輸入再輸出等邏輯。

結(jié)構(gòu)方法從語法和語義兩個(gè)維度支持面向?qū)ο笠?guī)范,那么構(gòu)造函數(shù)應(yīng)該怎么做才能實(shí)現(xiàn)面向?qū)ο螅?/p>

顧名思義,構(gòu)造函數(shù)應(yīng)該是一個(gè)函數(shù),而不是一個(gè)方法。該方法由指向自身的參數(shù)組成。這一點(diǎn)不應(yīng)包含在構(gòu)造函數(shù)中。否則,應(yīng)該有對(duì)象的實(shí)例,并且會(huì)構(gòu)造紗線?

既然構(gòu)造函數(shù)是普通函數(shù),那么按照面向?qū)ο蟮拿s定,方法名應(yīng)該是結(jié)構(gòu)體名,但是如果你真的操作了,編輯器會(huì)直接報(bào)錯(cuò),所以這不符合到面向?qū)ο蟮拿s定!

這樣構(gòu)造函數(shù)的名字可能不是結(jié)構(gòu)類型的名字,而是其他特殊的名字。最好能通過名字知道名字,并且有在實(shí)例化對(duì)象時(shí)自動(dòng)調(diào)用的能力。

當(dāng)然,這個(gè)名字取決于 Go 的設(shè)計(jì)者如何命名。在這里靠猜測(cè)很難猜到,否則我就是設(shè)計(jì)師!

另外,還有一種可能,就是Go沒有構(gòu)造函數(shù)。如果要實(shí)現(xiàn)構(gòu)造函數(shù)的邏輯,只能另尋他路了。

有什么可靠的依據(jù)嗎?

我認(rèn)為這是可能的。構(gòu)造函數(shù)雖然提供了自動(dòng)初始化的能力,但如果真的在構(gòu)造函數(shù)中加入復(fù)雜的初始化邏輯,無疑會(huì)增加日后排查的難度,帶來一定的用戶。閱讀障礙,所以在某種程度上,構(gòu)造函數(shù)很可能被濫用!

這是否意味著不需要構(gòu)造函數(shù)?

不能說同樣的話。除了基本的變量初始化和簡(jiǎn)單的邏輯之外,構(gòu)造函數(shù)在實(shí)際編程中還有一定的用途。為了避免濫用,直接禁用。有點(diǎn)像喝毒解渴的感覺吧?

因此,我個(gè)人的觀點(diǎn)是,構(gòu)造函數(shù)的初始化邏輯應(yīng)該保留,或者可以用其他方式實(shí)現(xiàn),或者干脆放棄構(gòu)造函數(shù),讓編譯器自動(dòng)實(shí)現(xiàn)構(gòu)造函數(shù),就像編譯器可以自動(dòng)添加一樣就像多個(gè)字段之間的分號(hào)。

如果開發(fā)者真的需要構(gòu)造函數(shù),結(jié)構(gòu)體初始化的邏輯總是可以通過工廠模式或者單例模式自定義,所以放棄也可以!

最后,以上純屬個(gè)人猜想。不知道Go中有沒有構(gòu)造函數(shù)。如果你知道,請(qǐng)清楚地告訴我答案。我個(gè)人傾向于沒有構(gòu)造函數(shù),最多只提供類似的構(gòu)造函數(shù)初始化。邏輯!

現(xiàn)在,我們已經(jīng)封裝了結(jié)構(gòu)體的數(shù)據(jù),定義了結(jié)構(gòu)體的方法,實(shí)現(xiàn)了結(jié)構(gòu)體的工廠函數(shù)。那么讓我們繼續(xù)完善動(dòng)態(tài)數(shù)組,實(shí)現(xiàn)數(shù)組的基本操作。

func NewMyDynamicArray() *MyDynamicArray {
	var myDynamicArray MyDynamicArray
	myDynamicArray.len = 0
	myDynamicArray.cap = 10
	var arr [10]int
	myDynamicArray.ptr = &arr
	return &myDynamicArray
}
func TestMyDynamicArray(t *testing.T) {
	myDynamicArray := NewMyDynamicArray()
	t.Log(myDynamicArray)
}

首先將測(cè)試用例中的邏輯提取到工廠函數(shù)中。不帶參數(shù)的工廠函數(shù)初始化的默認(rèn)內(nèi)部數(shù)組長(zhǎng)度為10,然后再考慮調(diào)用者的規(guī)范和動(dòng)態(tài)數(shù)組函數(shù)的實(shí)現(xiàn),暫時(shí)實(shí)現(xiàn)最基本的功能。 .

初始化的內(nèi)部數(shù)組都是零值,所以需要先提供外界可以添加的接口,實(shí)現(xiàn)如下:


func (myArr *MyDynamicArray) Add(index, value int) {
	if myArr.len == myArr.cap {
		return
	}
	if index < 0 || index > myArr.len {
		return
	}
	for i := myArr.len - 1; i >= index; i-- {
		(*myArr.ptr)[i+1] = (*myArr.ptr)[i]
	}
	(*myArr.ptr)[index] = value
	myArr.len++
}

由于默認(rèn)的初始化工廠函數(shù)暫時(shí)是一個(gè)定長(zhǎng)數(shù)組,所以新元素實(shí)際上是一個(gè)定長(zhǎng)數(shù)組,但這并不妨礙動(dòng)態(tài)數(shù)組部分的后續(xù)實(shí)現(xiàn)。

為了方便操作,提供了插入頭部和插入尾部?jī)蓚€(gè)接口,可以實(shí)現(xiàn)更高級(jí)的基于動(dòng)態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu)。

func (myArr *MyDynamicArray) AddLast(value int) {
	myArr.Add(myArr.len, value)
}
func (myArr *MyDynamicArray) AddFirst(value int) {
	myArr.Add(0, value)
}

為了測(cè)試動(dòng)態(tài)數(shù)組的算法是否正確,提供了打印方法查看數(shù)組的結(jié)構(gòu)。

可以看出打印方式顯示的數(shù)據(jù)結(jié)構(gòu)和真實(shí)的結(jié)構(gòu)數(shù)據(jù)是一樣的,接下來我們更有信心繼續(xù)封裝動(dòng)態(tài)數(shù)組!

func (myArr *MyDynamicArray) Set(index, value int) {
	if index < 0 || index >= myArr.len {
		return
	}
	(*myArr.ptr)[index] = value
}
func (myArr *MyDynamicArray) Get(index int) int {
	if index < 0 || index >= myArr.len {
		return -1
	}
	return (*myArr.ptr)[index]
}

這兩個(gè)接口比較簡(jiǎn)單,更新數(shù)組指定索引的元素,根據(jù)索引查詢數(shù)組的值。

接下來,我們開始測(cè)試動(dòng)態(tài)數(shù)組的所有接口!

動(dòng)態(tài)數(shù)組暫時(shí)告一段落。不知道大家有沒有好奇我們?yōu)槭裁从脛?dòng)態(tài)數(shù)組作為例子來解釋面向?qū)ο螅?/p>

其實(shí)主要是驗(yàn)證上一篇的猜想,即切片和數(shù)組是什么關(guān)系?

我認(rèn)為切片的底層是一個(gè)數(shù)組,但是語法層面提供了支持,讓你看不到數(shù)組的陰影。既然仙女已經(jīng)學(xué)會(huì)了面向?qū)ο?,那么就用面向?qū)ο蟮姆绞絹韺?shí)現(xiàn)切片的功能,雖然不能模擬語法層面的實(shí)現(xiàn),但是功能特性是可以模仿的!

以下是對(duì)本文知識(shí)點(diǎn)的總結(jié),即封裝的實(shí)現(xiàn)。

如何封裝結(jié)構(gòu)

之所以叫結(jié)構(gòu)體,是因?yàn)镚o的關(guān)鍵字不是,而且也是面向?qū)ο缶幊田L(fēng)格中唯一支持的特性。不支持繼承和多態(tài),我會(huì)開一篇文章詳細(xì)說明。

結(jié)構(gòu)是封裝數(shù)據(jù)的一種手段。結(jié)構(gòu)體只能定義數(shù)據(jù),不能定義方法。這些數(shù)據(jù)有時(shí)稱為字段,有時(shí)稱為屬性或簡(jiǎn)稱為變量。至于叫什么,也沒什么特別的。重要的是,如何命名與環(huán)境的語義有關(guān)。

type MyDynamicArray struct {
	ptr *[10]int
	len int
	cap int
}

這個(gè)結(jié)構(gòu)中有三個(gè)變量。變量由換行符而不是分號(hào)和換行符分隔。一開始感覺有點(diǎn)奇怪,不過編輯器一般都很聰明。如果你習(xí)慣性地加分號(hào),會(huì)提示你刪除,所以不用在意語法細(xì)節(jié)。

結(jié)構(gòu)不支持寫函數(shù),只支持?jǐn)?shù)據(jù)結(jié)構(gòu),也就是說數(shù)據(jù)和行為是分離的,兩者的關(guān)系比較弱。

func (myArr *MyDynamicArray) IsEmpty() bool {
	return myArr.len == 0
}

這種方式的功能與普通功能略有不同。包含結(jié)構(gòu)變量的參數(shù)被推進(jìn)到函數(shù)名的前面。語義也很清楚。它是指結(jié)構(gòu)的功能。為了區(qū)別于普通函數(shù),這種函數(shù)被稱為方法。

其實(shí)就簡(jiǎn)單的實(shí)現(xiàn)函數(shù)而言,方法和函數(shù)沒有區(qū)別,無非就是調(diào)用者的使用方式!

func IsEmpty(myArr *MyDynamicArray) bool {
	return myArr.len == 0
}

之所以采用這種設(shè)計(jì)方式,一方面是體現(xiàn)了函數(shù)的重要性,畢竟在Go語言中它們是一等公民!

另一方面是為了實(shí)現(xiàn)面向?qū)ο蟮恼Z法習(xí)慣,不管是屬性還是方法面向?qū)ο缶幊陶Z言,都用點(diǎn)號(hào)調(diào)用。操作員。

在官方文檔中,這個(gè)結(jié)構(gòu)參數(shù)被稱為接收者,因?yàn)閿?shù)據(jù)和行為是弱相關(guān)的。發(fā)送數(shù)據(jù)的人是誰?

不言而喻,發(fā)送方應(yīng)該是調(diào)用方傳遞過來的結(jié)構(gòu)體實(shí)例對(duì)象,結(jié)構(gòu)體變量將數(shù)據(jù)結(jié)構(gòu)體發(fā)送給接收方方法,從而將數(shù)據(jù)和行為聯(lián)系在一起。

func TestMyDynamicArray(t *testing.T) {
	myDynamicArray := NewMyDynamicArray()
	fmt.println(myDynamicArray.IsEmpty())
}

好的,以上就是第一次面向?qū)ο篌w驗(yàn)的所有部分。這只是很小的一部分,我花了三天時(shí)間。我想說的是,轉(zhuǎn)變思維不容易,寫好文章也不容易。 !

在下一篇文章中,我會(huì)繼續(xù)介紹面向?qū)ο蟮陌b特性,講解更多干貨。如果您覺得本文對(duì)您有幫助,請(qǐng)轉(zhuǎn)發(fā)您的評(píng)論,感受您的閱讀!

文章標(biāo)題:,,繼承的特性子類Go-?Go
網(wǎng)頁鏈接:http://www.muchs.cn/news48/296798.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供標(biāo)簽優(yōu)化、搜索引擎優(yōu)化、企業(yè)建站、做網(wǎng)站、電子商務(wù)、自適應(yīng)網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

搜索引擎優(yōu)化