GO是否是面向對象的語言?程序員
GO做者如是說:「是,也不是。」編程
正如前面所說:GO是一種面向類型的語言,它有類型和方法,但沒有類的概念,程序員能夠用一種面向對象的風格(或者說是方式)來編程,下面咱們從封裝性、繼承性和多態性三大面向對象的特性談談GO語言iphone
一、封裝性ide
面向對象的語言中,「類」是基本單位,它把屬性、方法侷限在「類」中,並對外提供公共方法讓使用者操做對象。固然這一過程離不開修飾符:public、protected、private等。函數
GO語言如何實現封裝性呢?它是經過結構體(struct)和爲類型添加方法的方式實現的。測試
例如,封裝一個矩形(Rect)類,試想調用者會對矩形類作什麼操做呢?無非就是看看它的面積、周長之類的信息,那麼按照面向對象的編程思想來講,只要向使用者暴露getArea()、getPerimeter()方法便可,使用者無須接觸到矩形的長和寬。ui
/**spa * 定義一個結構體,裏面有兩個成員length和width3d */orm type Rect struct { length, width int } |
接下來爲結構體定義兩個方法getArea()和getPerimeter(),分別用來讀取矩形的面積和周長
/** * 獲取矩形的面積 */ func (r *Rect) GetArea() int { return r.length * r.width } /** * 獲取矩形的周長 */ func (r *Rect) GetPerimeter() int { return (r.length + r.width) * 2 } |
爲了讓該包以外的函數調用到GetArea()和GetPerimeter(),因此這裏函數的首字母大寫。同時爲告終構體初始化更面向對象些,再定義一個用於初始化結構體的方法NewRect()
/** * 初始化結構體Rect */ func NewRect(length, width int) *Rect { return &Rect{length, width} } |
通過這樣的封裝,使用者能夠以面向對象的方式調用Rect了 :)
/** * 使用者先引入Rect.go的路徑 */ import ( "cube" "fmt" ) /** * 經過cube調用NewRect()生成*Rect對象 */ func main() { r := cube.NewRect(10, 20) fmt.Println("面積:", r.GetArea(), " 周長:", r.GetPerimeter()) } |
【備註】:
Rect.go和測試main.go路徑結構以下
其中Rect.go所屬包爲cube、main.go所屬包爲main
執行程序,運行結果以下:
二、繼承性
rect結構體定義兩個方法,分別用於獲取面積和周長,cube結構體也定義了兩個方法,一個是獲取體積,另外一個重寫父結構體rect的獲取周長:
(1)在cube目錄下建立rect.go文件,裏面寫rect代碼
// 讓rect結構體在cube包內 package cube // 定義rect結構體 type rect struct{ length, width int } /** * 獲取矩形的面積 */ func (r Rect) GetArea() int { return r.length * r.width } /** * 獲取矩形的周長 */ func (r Rect) GetPerimeter() int { return (r.length + r.width) * 2 } |
(2)在cube目錄下建立cube.go文件,裏面寫cube代碼
// 讓Cube結構體在cube包內 package cube // 因爲Cube結構體須要對外,因此首字母大寫 type Cube struct { Rect // 這裏經過嵌套結構體實現GO的繼承 height int } /** * 獲取立方體的體積 */ func (c Cube) GetVolume() int { return c.Rect.length * c.Rect.width * c.height } /** * 重寫父類獲取周長方法 */ func (c Cube) GetPerimeter() int { return (c.Rect.length + c.Rect.width + c.height) * 4 } /** * 爲了更象面向對象編程些,這裏定義了一個方法獲取Cube對象 */ func NewCube(length, width, height int) Cube { return Cube{Rect: Rect{length, width}, height: height} } |
(3)在src目錄下建立main.go文件,該文件與cube目錄同級,裏面寫測試代碼
// 讓main()方法的包爲main package main import ( "cube" // 因爲要用到上面定義的Cube結構體,因此須要引入Cube結構體所屬包 "fmt" ) func main() { var c cube.Cube = cube.NewCube(10, 20, 30) // 經過包名調用cube.go定義的對外方法NewCube() fmt.Println("面積:", c.GetArea(), ",體積:", c.GetVolume()) // 經過變量c調用相應方法 fmt.Println("周長:", c.GetPerimeter()) } |
執行go run main.go,獲得執行結果:
從運行結果能夠看到,儘管Cube沒有定義GetArea()方法,但經過c.GetArea()的確調用到了同時並打印出結果;因爲Cube重寫了GetPerimeter()方法,從結果來看c.GetPerimeter()執行的是Cube的GetPerimeter()就去。
從該例也不難看出GO的繼承性是經過結構的嵌套來實現的
三、多態性
多態意味着一個對象有多重特徵,在特定的狀況下表現不一樣的狀態,即對應着不一樣的方法
Mp3和Iphone都實現了USB接口,並分別實現接口USB定義的方法,當面向對象如此調用時:
USB u1 = new Mp3();
u1.connect(); // 打印出「mp3」
USB u2 = new Iphone();
u2.connect(); // 打印出「iphone」
一樣的接口(USB)對象(u1, u2),因爲實現類不一樣,調用相同的方法(connect()),最終的效果是不一樣的,這就是多態的做用,通常用於「控制反轉」。
那麼Go呢?
Go能夠經過Interface、struct模擬實現多態
在src下建立usb目錄,在usb目錄下建立usb.go文件,裏面定義USB接口
// 把接口USB放在usb包中 package usb // 定義USB接口,裏面只有一個Connect()方法 type USB interface { Connect() } |
在usb目錄下建立mp3.go文件,裏面定義Mp3結構體,併爲該結構體增長Connect(),這樣就至關於實現了接口USB
// 把Mp3結構體放在usb包中 package usb import ( "fmt" ) // 定義Mp3空結構體 type Mp3 struct { } // 爲Mp3增長Connect()方法,這樣就缺省實現了USB接口 func (m Mp3) Connect() { fmt.Println("mp3") } |
一樣,在usb目錄下建立iphone.go文件,裏面定義Iphone結構體
// 把Iphone結構體放在usb包中 package usb import ( "fmt" ) // 定義Iphone空結構體 type Iphone struct { } // 爲Iphone增長Connect()方法,這樣就缺省實現了USB接口 func (i Iphone) Connect() { fmt.Println("iphone") } |
下面演示GO語言的多態性:
在src目錄下建立main.go文件,該文件與usb目錄同級,裏面寫測試代碼
package main import ( "usb" ) func main() { var m usb.USB = usb.Mp3{} m.Connect() var n usb.USB = usb.Iphone{} n.Connect() } |
執行程序,運行結果以下:
【備註】:
關於本文的演示代碼能夠在本章節源代碼處下載