go語言包含了對oop語言的支援,接下來我們來看看go語言中的方法。
儘管沒有被大眾所接受的明確的oop的定義,從我們的理解來講,乙個物件其實也就是乙個
簡單的值或者乙個變數,在這個物件中會包含一些方法,而乙個方法則是乙個乙個和特殊類
型關聯的函式。乙個物件導向的程式會用方法來表達其屬性和對應的操作,這樣使用這個對
象的使用者就不需要直接去操作物件,而是借助方法來做這些事情。
package geometry
import "math"
type point struct
// traditional function
func distance(p, q point) float64
// same thing, but as a method of the point type
func (p point) distance(q point) float64
上面的**裡那個附加的引數p,叫做方法的接收器(receiver),早期的物件導向語言留下的遺
產將呼叫乙個方法稱為「向乙個物件傳送訊息」。
在go語言中,我們並不會像其它語言那樣用this或者self作為接收器;我們可以任意的選擇接
收器的名字。由於接收器的名字經常會被使用到,所以保持其在方法間傳遞時的一致性和簡
短性是不錯的主意。這裡的建議是可以使用其型別的第乙個字母,比如這裡使用了point的首
字母p。
在方法呼叫過程中,接收器引數一般會在方法名之前出現。這和方法宣告是一樣的,都是接
收器引數在方法名字之前。
p := point
q := point
fmt.println(distance(p, q)) // "5", function call
fmt.println(p.distance(q)) // "5", method call
可以看到,上面的兩個函式呼叫都是distance,但是卻沒有發生衝突。第乙個distance的呼叫
實際上用的是包級別的函式geometry.distance,而第二個則是使用剛剛宣告的point,呼叫的
是point類下宣告的point.distance方法。
這種p.distance的表示式叫做選擇器,因為他會選擇合適的對應p這個物件的distance方法來
執行。選擇器也會被用來選擇乙個struct型別的字段,比如p.x。由於方法和字段都是在同一
命名空間,所以如果我們在這裡宣告乙個x方法的話,編譯器會報錯,因為在呼叫p.x時會有
歧義。因為每種型別都有其方法的命名空間,我們在用distance這個名字的時候,不同的distance調
用指向了不同型別裡的distance方法。
在能夠給任意型別定義方法這一點上,go和很多其它的物件導向的語言不太一樣。因此
在go語言裡,我們為一些簡單的數值、字串、slice、map來定義一些附加行為很方便。方
法可以被宣告到任意型別,只要不是乙個指標或者乙個inte***ce。
當呼叫乙個函式時,會對其每乙個引數值進行拷貝,如果乙個函式需要更新乙個變數,或者
函式的其中乙個引數實在太大我們希望能夠避免進行這種預設的拷貝,這種情況下我們就需
要用到指標了。
對應到我們這裡用來更新接收器的物件的方法,當這個接受者變數本身比較
大時,我們就可以用其指標而不是物件來宣告方法,如下:
func (p *point) scaleby(factor float64)
這個方法的名字是 (*point).scaleby 。這裡的括號是必須的;沒有括號的話這個表示式可能
會被理解為 *(point.scaleby)。
實際上注意兩點:
不管你的method的receiver是指標型別還是非指標型別,都是可以通過指標/非指標型別
進行呼叫的,編譯器會幫你做型別轉換。
在宣告乙個method的receiver該是指標還是非指標型別時,你需要考慮兩方面的內部,第
一方面是這個物件本身是不是特別大,如果宣告為非指標變數時,呼叫會產生一次拷
貝;第二方面是如果你用指標型別作為receiver,那麼你一定要注意,這種指標型別指向
的始終是一塊記憶體位址,就算你對其進行了拷貝。熟悉c或者c艹的人這裡應該很快能明白。
nil也是乙個合法的接收器型別
就像一些函式允許nil指標作為引數一樣,方法理論上也可以用nil指標作為其接收器,尤其當
nil對於物件來說是合法的零值時,比如map或者slice。
我們經常選擇乙個方法,並且在同乙個表示式裡執行,比如常見的p.distance()形式,實際上
將其分成兩步來執行也是可能的。p.distance叫作「選擇器」,選擇器會返回乙個方法"值"->一
個將方法(point.distance)繫結到特定接收器變數的函式。這個函式可以不通過指定其接收器
即可被呼叫;即呼叫時不需要指定接收器(譯註:因為已經在前文中指定過了),只要傳入函式
的引數即可:
p := point
q := point
distancefromp := p.distance // method value
fmt.println(distancefromp(q)) // "5"
var origin point //
fmt.println(distancefromp(origin)) // "2.23606797749979", sqrt(5)
scalep := p.scaleby // method value
scalep(2) // p becomes (2, 4)
scalep(3) // then (6, 12)
scalep(10) // then (60, 120)
乙個物件的變數或者方法如果對呼叫方是不可見的話,一般就被定義為「封裝」。封裝有時候也
被叫做資訊隱藏,同時也是物件導向程式設計最關鍵的乙個方面。
go語言只有一種控制可見性的手段:大寫首字母的識別符號會從定義它們的包中被匯出,小寫
字母的則不會。這種限制包內成員的方式同樣適用於struct或者乙個型別的方法。因而如果我
們想要封裝乙個物件,我們必須將其定義為乙個struct。
例如:type intset struct
這種基於名字的手段使得在語言中最小的封裝單元是package,而不是像其它語言一樣的類
型。乙個struct型別的字段對同乙個包的所有**都有可見性,無論你的**是寫在乙個函式
還是乙個方法裡。
封裝提供了三方面的優點。首先,因為呼叫方不能直接修改物件的變數值,其只需要關注少
量的語句並且只要弄懂少量變數的可能的值即可。
第二,隱藏實現的細節,可以防止呼叫方依賴那些可能變化的具體實現,這樣使設計包的程
序員在不破壞對外的api情況下能得到更大的自由。
把bytes.buffer這個型別作為例子來考慮。這個型別在做短字串疊加的時候很常用,所以在
設計的時候可以做一些預先的優化,比如提前預留一部分空間,來避免反覆的記憶體分配。又
因為buffer是乙個struct型別,這些額外的空間可以用附加的位元組陣列來儲存,且放在乙個小
寫字母開頭的字段中。這樣在外部的呼叫方只能看到效能的提公升,但並不會得到這個附加變數。
介面型別是對其它型別行為的抽象和概括;因為介面型別不會和特定的實現細節繫結在一
起,通過這種抽象的方式我們可以讓我們的函式更加靈活和更具有適應能力。
很多物件導向的語言都有相似的介面概念,但go語言中介面型別的獨特之處在於它是滿足隱
式實現的。也就是說,我們沒有必要對於給定的具體型別定義所有滿足的介面型別;簡單地
擁有一些必需的方法就足夠了。
這種設計可以讓你建立乙個新的介面型別滿足已經存在的具
體型別卻不會去改變這些型別的定義;當我們使用的型別來自於不受我們控制的包時這種設
計尤其有用。
Go語言與資料庫開發 01 03
在本文中,我們將介紹go的基礎資料型別。雖然從底層而言,所有的資料都是由位元組成,但計算機一般操作的是固定大小的數,如整 數 浮點數 位元陣列 記憶體位址等。進一步將這些數組織在一起,就可表達更多的物件,例如資料報 畫素點 詩歌,甚至其他任何物件。go語言提供了豐富的資料組織形式,這依 賴於go語言...
go語言連線mongodb資料庫
命令列下輸入 go get gopkg.in mgo.v2 2 引入mongodb包 import gopkg.in mgo.v2 gopkg.in mgo.v2 bson 3 完整 packagemain import gopkg.in mgo.v2 gopkg.in mgo.v2 bson fm...
Go語言使用PostgreSQL資料庫
postgresql和mysql比較,它更加龐大一點,因為它是用來替代oracle而設計的。所以在企業應用中採用postgresql是乙個明智的選擇。現在mysql被oracle收購之後,有傳聞oracle正在逐步的封閉mysql,鑑於此,將來我們也許會選擇postgresql而不是mysql作為專...