Go語(yǔ)言面向?qū)ο?/h1>
Go語(yǔ)言面向?qū)ο?/strong>
①Golang也支持面向?qū)ο缶幊蹋∣OP)。
②Golang沒有類(class),Go語(yǔ)言的結(jié)構(gòu)體(struct)和其它編程語(yǔ)言的類(class)有同等的地位,可以理解成Golang是基于struct來(lái)實(shí)現(xiàn)OOP特性的。
③Golang去掉了傳統(tǒng)OOP語(yǔ)言的繼承、方法重載、構(gòu)造函數(shù)、析構(gòu)函數(shù)、隱藏的this指針等。
④Golang有OOP的繼承,封裝、多態(tài)的特性,只是實(shí)現(xiàn)的方式不一樣。
⑤OOP是語(yǔ)言類型系統(tǒng)(type system)的一部分,通過接口(interface)關(guān)聯(lián),耦合性低,也非常靈活。在Golang 中面向接口編程是非常重要的特性。
?
聲明:
type結(jié)構(gòu)體名稱?struct
{field1 type
field2 type
}
?
字段/屬性:
①?gòu)母拍钌峡矗航Y(jié)構(gòu)體字段?=?屬性 =?field?(即授課中,統(tǒng)一叫字段)
②字段是結(jié)構(gòu)體的一個(gè)組成部分,一般是基本數(shù)據(jù)類型、數(shù)組,也可是引用類型。
③創(chuàng)建結(jié)構(gòu)體變量后,若未賦值,則為默認(rèn)值:
布爾類型是 false,數(shù)值是0,字符串是""。score [3]int 則為[0,0,0]
指針,slice, map的零值都是nil,即還沒有分配空間。
④不同結(jié)構(gòu)體變量的字段是獨(dú)立,互不影響,一個(gè)結(jié)構(gòu)體變量字段的更改,不影響另外一個(gè),結(jié)構(gòu)體是值類型。

?
?
創(chuàng)建結(jié)構(gòu)體變量、訪問結(jié)構(gòu)體字段
①var person Person
②var person Person = Person{}:“p2 := Person{ "mary",20}”
③var person *Person = new (Person)
(*p3).Name = "smith"
p3.Name = "john"
④var person *Person =&Person{}
A.var person *Person =&Person{ "mary",20}
B.(*person).Name = "scott"
person. Name = "scott~~"
③④返回的是結(jié)構(gòu)體指針。
Go支持結(jié)構(gòu)體指針.字段名。go編譯器底層對(duì)person.Name做了轉(zhuǎn)化(*person).Name。
?
struct類型的內(nèi)存分配機(jī)制


①結(jié)構(gòu)體的所有字段在內(nèi)存中是連續(xù)的。
②結(jié)構(gòu)體是用戶單獨(dú)定義的類型,和其它類型進(jìn)行轉(zhuǎn)換時(shí),需要有完全相同的字段(名字、個(gè)數(shù)、類型)
③結(jié)構(gòu)體進(jìn)行type重新定義(相當(dāng)于取別名),Golang認(rèn)為是新的數(shù)據(jù)類型,但是相互間可以強(qiáng)轉(zhuǎn)。stu2 =Stu(stu1)
④ struct的每個(gè)字段上,可以寫上一個(gè)tag。該tag可以通過反射機(jī)制獲取,常見的使用場(chǎng)景就是序列化和反序列化。

方法:
?

①給Person結(jié)構(gòu)體添加speak方法,輸出xxx是一個(gè)好人。
②給Person結(jié)構(gòu)體添加jisuan方法,可接收一個(gè)數(shù)n,計(jì)算從1+..+n的結(jié)果,說明方法體內(nèi)可進(jìn)行運(yùn)算。
方法的調(diào)用和傳參機(jī)制原理:【重要!】
①變量調(diào)用方法時(shí),該變量本身也會(huì)作為一個(gè)參數(shù)傳遞到方法(如果變量是值類型,則進(jìn)行值拷貝,如果變量是引用類型,則進(jìn)行地質(zhì)拷貝)
②結(jié)構(gòu)體類型是值類型,在方法調(diào)用中,遵守值類型的傳遞機(jī)制,是值拷貝傳遞方式。
③如程序員希望在方法中,修改結(jié)構(gòu)體變量的值,可以通過結(jié)構(gòu)體指針的方式來(lái)處理。
④Golang中的方法是作用在指定的數(shù)據(jù)類型上的(即:和指定的數(shù)據(jù)類型綁定),因此自定義類型,都可以有方法,而不僅僅是struct。
⑤方法的訪問范圍控制的規(guī)則。方法名首字母小寫,只能在本包訪問,方法首字母大寫,可以在本包和其它包訪問。
⑥如果一個(gè)類型實(shí)現(xiàn)了String()這個(gè)方法,那么fmt.Println默認(rèn)會(huì)調(diào)用這個(gè)變量的String()進(jìn)行輸出。
?
方法和函數(shù)的區(qū)別:
①調(diào)用方式不同:
函數(shù):函數(shù)名(實(shí)參列表)
方法:變量.方法名(實(shí)參列表)
②對(duì)于普通函數(shù),接收者為值類型時(shí),不能將指針類型的數(shù)據(jù)直接傳遞,反之亦然。
③對(duì)于方法,接收者為值類型時(shí),可以直接用指針類型的變量調(diào)用方法,反過來(lái)同樣也可以。
?

不管調(diào)用形式如何,真正決定是值拷貝還是地址拷貝,看這個(gè)方法是和哪個(gè)類型綁定。
若是和值類型,比如(p Person),則是值拷貝;
若和指針類型,比如(p *Person),則是地址拷貝。
?
案例:
①編寫一個(gè)Student結(jié)構(gòu)體,包含name、gender、age、id、score字段,分別為 string、string、int、int、float64類型。
②結(jié)構(gòu)體中聲明一個(gè)say方法,返回string類型,方法返回信息中包含所有字段值。
③在main方法中,創(chuàng)建Student結(jié)構(gòu)體實(shí)例(變量),并訪問 say方法,將結(jié)果打印輸出。
?

?創(chuàng)建結(jié)構(gòu)體變量時(shí)指定字段值:


Golang的結(jié)構(gòu)體沒有構(gòu)造函數(shù),通??梢允褂霉S模式來(lái)解決這個(gè)問題。
使用工廠模式實(shí)現(xiàn)跨包創(chuàng)建結(jié)構(gòu)體實(shí)例(變量)的案例:
如果model包的結(jié)構(gòu)體變量首字母大寫,引入后,直接使用,沒有問題。
如果model包的結(jié)構(gòu)體變量首字母小寫,引入后,不能直接使用,可以工廠模式解決。
?
抽象:
我們?cè)谇懊嫒ザx一個(gè)結(jié)構(gòu)體時(shí)候,實(shí)際上就是把一類事物的共有的屬性(字段)和行為(方法)提取出來(lái),形成一個(gè)物理模型(結(jié)構(gòu)體)。這種研究問題的方法稱為抽象。
?
封裝:
封裝(encapsulation)就是把抽象出的字段和對(duì)字段的操作封裝在一起,數(shù)據(jù)被保護(hù)在內(nèi)部,程序的其它包只有通過被授權(quán)的操作(方法),才能對(duì)字段進(jìn)行操作。
?
①將結(jié)構(gòu)體、字段(屬性)的首字母小寫(不能導(dǎo)出了,其它包不能使用,類似private)
②給結(jié)構(gòu)體所在包提供一個(gè)工廠模式的函數(shù),首字母大寫。類似一個(gè)構(gòu)造函數(shù)
③提供一個(gè)首字母大寫的Set方法(類似其它語(yǔ)言的public),用于對(duì)屬性判斷并賦值
func (var結(jié)構(gòu)體類型名) Get.Xxx(參數(shù)列表)(返回值列表){
//加入數(shù)據(jù)驗(yàn)證的業(yè)務(wù)邏輯
var.字段?=?參數(shù)
}
④提供一個(gè)首字母大寫的Get方法(類似其它語(yǔ)言的 public),用于獲取屬性的值
func (var結(jié)構(gòu)體類型名)GetXxx()?{
return var.age;
}
?
繼承:
嵌套匿名結(jié)構(gòu)體的基本語(yǔ)法
type Goods struct
{Name string
Price int
}
type Book stiuct {
Goods //嵌套匿名結(jié)構(gòu)體Goods
Writer string
}
①結(jié)構(gòu)體可以使用嵌套匿名結(jié)構(gòu)體所有的字段和方法,即:首字母大寫或者小寫的字段、方法,都可以使用。
?
②匿名結(jié)構(gòu)體字段訪問可以簡(jiǎn)化。

?
當(dāng)我們直接通過 b訪問字段或方法時(shí),其執(zhí)行流程如下比如 b.Name:
編譯器會(huì)先看b對(duì)應(yīng)的類型有沒有Name,如果有,則直接調(diào)用B類型的Name字段
如果沒有,就去看B中嵌入的匿名結(jié)構(gòu)體A有沒有聲明Name字段,如果有就調(diào)用,如果沒有繼續(xù)查找….如果都找不到就報(bào)錯(cuò)。
?
③當(dāng)結(jié)構(gòu)體和匿名結(jié)構(gòu)體有相同的字段或者方法時(shí),編譯器采用就近訪問原則訪問,如希望訪問匿名結(jié)構(gòu)體的字段和方法,可以通過匿名結(jié)構(gòu)體名來(lái)區(qū)分。
?
④結(jié)構(gòu)體嵌入兩個(gè)(或多個(gè))匿名結(jié)構(gòu)體,如兩個(gè)匿名結(jié)構(gòu)體有相同的字段和方法(同時(shí)結(jié)構(gòu)體本身沒有同名的字段和方法),在訪問時(shí),就必須明確指定匿名結(jié)構(gòu)體名字,否則編譯報(bào)錯(cuò)。
?

⑤如果一個(gè)struct嵌套了一個(gè)有名結(jié)構(gòu)體,這種模式就是組合,如果是組合關(guān)系,那么在訪問組合的結(jié)構(gòu)體的字段或方法時(shí),必須帶上結(jié)構(gòu)體的名字。
?

⑥嵌套匿名結(jié)構(gòu)體后,也可以在創(chuàng)建結(jié)構(gòu)體變量(實(shí)例)時(shí),直接指定各個(gè)匿名結(jié)構(gòu)體字段的值。
?如果一個(gè)結(jié)構(gòu)體有int類型的匿名字段,就不能有第二個(gè)。
如果需要有多個(gè)int的字段,則必須給int字段指定名字。
?



如一個(gè)struct 嵌套了多個(gè)匿名結(jié)構(gòu)體,那么該結(jié)構(gòu)體可以直接訪問嵌套的匿名結(jié)構(gòu)體的字段和方法,從而實(shí)現(xiàn)了多重繼承。
如嵌入的匿名結(jié)構(gòu)體有相同的字段名或者方法名,則在訪問時(shí),需要通過匿名結(jié)構(gòu)體類型名來(lái)區(qū)分。
?
接口:


interface類型可以定義一組方法,但是這些不需要實(shí)現(xiàn)。并且 interface不能包含任何變量。到某個(gè)自定義類型(比如結(jié)構(gòu)體 Phone)要使用的時(shí)候,在根據(jù)具體情況把這些方法寫出來(lái)(實(shí)現(xiàn))。
①接口里的所有方法都沒有方法體,即接口的方法都是沒有實(shí)現(xiàn)的方法。接口體現(xiàn)了程序設(shè)計(jì)的多態(tài)和高內(nèi)聚低偶合的思想。
②Golang中的接口,不需要顯式的實(shí)現(xiàn)。只要一個(gè)變量,含有接口類型中的所有方法,那么這個(gè)變量就實(shí)現(xiàn)這個(gè)接口。因此,Golang中沒有implement這樣的關(guān)鍵字。
?

③接口本身不能創(chuàng)建實(shí)例,但是可以指向一個(gè)實(shí)現(xiàn)了該接口的自定義類型的變量(實(shí)例)。
④一個(gè)自定義類型只有實(shí)現(xiàn)了某個(gè)接口,才能將該自定義類型的實(shí)例(變量)賦給接口類型。
⑤只要是自定義數(shù)據(jù)類型,就可以實(shí)現(xiàn)接口,不僅僅是結(jié)構(gòu)體類型。
?

⑥一個(gè)自定義類型可以實(shí)現(xiàn)多個(gè)接口。
?

⑦Golang接口中不能有任何變量。
⑧interface類型默認(rèn)是一個(gè)指針(引用類型),如果沒有對(duì)interface初始化就使用,那么會(huì)輸出nil。
⑨一個(gè)接口(比如A接口)可以繼承多個(gè)別的接口(比如B,C接口),這時(shí)如果要實(shí)現(xiàn)A接口,也必須將B,C接口的方法也全部實(shí)現(xiàn)。
?

⑩空接口interface{}沒有任何方法,所以所有類型都實(shí)現(xiàn)了空接口,即我們可以把任何一個(gè)變量賦給空接口。
?


?
接口編程:
實(shí)現(xiàn)對(duì)Hero結(jié)構(gòu)體切片的排序:sort.Sort(data Interface)



①當(dāng)A結(jié)構(gòu)體繼承了B結(jié)構(gòu)體,那么A結(jié)構(gòu)就自動(dòng)的繼承了B結(jié)構(gòu)體的字段和方法,并且可以直接使用。
②當(dāng)A結(jié)構(gòu)體需要擴(kuò)展功能,同時(shí)不希望去破壞繼承關(guān)系,則可以去實(shí)現(xiàn)某個(gè)接口即可,因此我們可以認(rèn)為:實(shí)現(xiàn)接口是對(duì)繼承機(jī)制的補(bǔ)充。
?
接口和繼承解決的解決的問題不同
繼承:解決代碼的復(fù)用性和可維護(hù)性。
接口:設(shè)計(jì)好各種規(guī)范(方法),讓其它自定義類型去實(shí)現(xiàn)這些方法。
接口比繼承更加靈活,繼承是滿足is - a的關(guān)系,而接口只需滿足like- a的關(guān)系。
接口在一定程度上實(shí)現(xiàn)代碼解耦。
多態(tài):
在Go語(yǔ)言,多態(tài)特征是通過接口實(shí)現(xiàn)的。按照統(tǒng)一的接口來(lái)調(diào)用不同的實(shí)現(xiàn)。這時(shí)接口變量就呈現(xiàn)不同的形態(tài)。
?
接口體現(xiàn)多態(tài)的兩種形式:
多態(tài)參數(shù):上圖usb接口變量就體現(xiàn)出多態(tài)的特點(diǎn)。

多態(tài)數(shù)組:如,給Usb數(shù)組中,存放Phone 結(jié)構(gòu)體和Camera結(jié)構(gòu)體變量。

?
類型斷言:
由于接口是一般類型,不知道具體類型,如果要轉(zhuǎn)成具體類型,就需要使用類型斷言。
在進(jìn)行類型斷言時(shí),如果類型不匹配,就會(huì)報(bào)panic,因此進(jìn)行類型斷言時(shí),要確保原來(lái)的空接口指向的就是斷言的類型。
如何在進(jìn)行斷言時(shí),帶上檢測(cè)機(jī)制,如果成功就ok,否則也不要報(bào)panic。
?


類型斷言的最佳實(shí)踐:編寫一個(gè)函數(shù),可以判斷輸入的參數(shù)是什么類型

?