人有的時候真的很賤,忙的時候想閑下來,閑的時候又很懷念忙的日子。疫情的這段時間待在家里遠程辦公,晚上空閑的時間相對較多,突然有種想學習一門新語言的沖動,選擇什么好呢?記得年前出差的產(chǎn)品部兄弟告訴我,年后他想學習一下go語言,不糾結,擼起袖子搞搞看。鼓搗了一周多,發(fā)現(xiàn)這個語言真有特點,這里談談go的面向?qū)ο髮崿F(xiàn)的奇特感受。
什么是面向?qū)ο?,滿足三大特性,封裝、繼承和多態(tài)。嚴格來講,go語言是沒有面向?qū)ο螅拖駝討B(tài)語言的世界里一直流傳著一種叫做鴨子類型的風格,“如果它走起來像鴨子,叫起來像鴨子,那它就是一只鴨子”,golang就是這樣的,沒有類,只有struct,且struct只擁有變量,不能帶有method,只能通過struct+method的巧妙方式去實現(xiàn),使其看起來具備OOP風格的“鴨子”。
- 封裝和繼承
封裝從字面上來理解就是包裝的意思,專業(yè)點就是信息隱藏,圈內(nèi)的行話就是封裝屬性和方法。繼承就是子類繼承父類的的屬性和方法,比較好理解。go語言里面的封裝和繼承,通過另外一種方式實現(xiàn),比較奇特,來一段代碼看看。
package main
import (
"fmt"
)
/*
Person結構體當成類
屬性:age name
方法:setName getName setAge printInfo
是不是有點奇怪和變態(tài) struct+method
*/
type Person struct {
age int
name string
}
func (this *Person)setName(tName string){
this.name=tName
}
func (this *Person)getName()(string){
return this.name
}
func (this *Person)setAge(tAge int){
this.age=tAge
}
func (this *Person)printInfo(){
fmt.Printf("Hello,my name is %s,I'm %d years old.\n",this.name,this.age);
}
/*
1.Student繼承Person同時擁有 Person下的所有的函數(shù)etName getName setAge printInfo
2.同時重載printInfo方法
*/
type Student struct {
Person
grad int
class int
}
func (s *Student)printInfo(){
fmt.Printf("Hello,I'm a student,my name is %s,I'm %d years old,I'm in class %d, grad %d .\n",s.name,s.age,s.class,s.grad);
}
func main(){
p:=Person{36,"Ap" }
p.printInfo()
p.setName("Yuki")
p.setAge(7)
p.printInfo()
s:=Student{p,1,3}
s.printInfo()
s.setName("Tom")
s.setAge(12)
s.printInfo()
}
##########go run main.go##########
Hello,my name is Ap,I'm 36 years old.
Hello,my name is Youki,I'm 7 years old.
Hello,I'm a student,my name is Yuki,I'm 7 years old,I'm in class 3, grad 1 .
Hello,I'm a student,my name is Tom,I'm 12 years old,I'm in class 3, grad 1 .
- 多態(tài)
go沒有 implements, extends 關鍵字,嚴格來說,多態(tài)與繼承、重載并不是孤立的,它們之間存在著緊密的聯(lián)系。在百度百科中的多態(tài)解釋,“在面向?qū)ο笳Z言中,接口的多種不同的實現(xiàn)方式即為多態(tài)。引用Charlie Calverts對多態(tài)的描述——多態(tài)性是允許你將父對象設置成為一個或更多的他的子對象相等的技術,賦值之后,父對象就可以根據(jù)當前賦值給它的子對象的特性以不同的方式運作;java中當子類擁有和父類同樣的函數(shù),當通過這個父類對象的引用調(diào)用這個函數(shù)的時候,調(diào)用到的是子類中的函數(shù)”(哇,好復雜!好繞口!),能不能簡單化一點,我的理解是,同一個函數(shù)定義接受為父類,調(diào)用時為子類的實例而執(zhí)行傳入實例下的動作。菜鳥教程解釋為,同一個接口,使用不同的實例而執(zhí)行不同操作(還是菜鳥說得好?。?。直接擼代碼,如下:
package main
import (
"fmt"
)
//相當于父類或者超類
type Human interface{
HelloWord()
}
func printfHello(h Human){
h.HelloWord()
}
//Student實現(xiàn)了Human的HellWord
type Student struct {
name string
}
func (s *Student)HelloWord(){
fmt.Printf("Hello ,I'm a student,my Name is %s.\n",s.name);
}
//Man實現(xiàn)了Human的HellWord
type Man struct {
name string
}
func (s *Man)HelloWord(){
fmt.Printf("Hello ,I'm a workman,my Name is %s.\n",s.name);
}
func main(){
s:=&Student{"Yuki"}
m:=&Man{"Ap"}
//多態(tài)
printfHello(s)
printfHello(m)
}
######go run main.go########
Hello ,I'm a student,my Name is Yuki.
Hello ,I'm a workman,my Name is Ap.
Human 是個接口,這里可以理解為其它語言中的父類,用interface來模仿superclass。 Student和Man實現(xiàn)了HelloWord方法,但是Human 怎么知道Student和Man已經(jīng)實現(xiàn)了它呢? 原因在于Student、Man實現(xiàn)了Human 中的全部方法, 這個時候就會認為子類。
golang社區(qū)很多數(shù)人認為,少了其它語言的implements/extends,讓go顯得更加優(yōu)雅。但是如果沒有implements 關鍵字,怎么知道m(xù)an和student,實現(xiàn)了human的接口,如果沒有文檔,又不熟悉接口類庫的話,感覺程序維護代碼的工作量會加重很多,優(yōu)雅從何談起呢?