Go語言面組合式向對象編程基礎總結


轉自:http://blog.csdn.net/yue7603835/article/details/44282823

Go語言的面向對象編程簡單而干凈,通過非侵入式接口模型,否定了C/C++ Java C#等傳統面向對象編程語言的復雜度的必要性,我們發現在Go中即使簡單的組合也能達到傳統面向對象語言的效果,而且耦合度非常低,按照Go的作者之一也就是C語言的作者之一說的一句話:Go是更好的C語言。
1、Go中任意類型Any即  interface{}類型,也就是空接口,可以賦值為任意類型
2、可以為其他類型 內置類型 不包括指針類型添加相應的方法 但是注意的一點是一定要用別名。。進行包裝
記住想要 為類型 添加新的方法 那么請把類型定義別名,別名和原來的類型就不一樣了成了新類型  
在Go中只能對非本地類型添加方法......也就是int不能添加方法 需要 type Int int 才可以為Int添加方法
package main
import(
   "fmt"
)
type Integer int
func (a Integer) Less(b Integer)bool {
    return a<b
}
func main(){
   var a Integer=1
   r:=a.Less(2)
   fmt.Println(r)
}
/////用法2 組合之指針
package main
import(
   "fmt"
)
type Integer int
func (a *Integer) Less(b Integer)bool {
    *a-=10
    return *a<b
}
 
type Int int
func (a *Int) LessIne(b Int)bool{
    return *a<b
}
func main(){
   var a Integer=1
   var aa int=10
   a=Integer(aa)
   r:=a.Less(2)
   fmt.Println(r)
}
 
3、go中不存在this指針,通過語法糖顯式傳遞對象
在Go語言中沒有隱藏的this指針”這句話的含義是:
 方法施加的目標(也就是“對象” )顯式傳遞,沒有被隱藏起來;
 方法施加的目標(也就是“對象” )不需要非得是指針,也不用非得叫this。 
4、用type定以后的別名類型就是新類型了 只有強制轉換才能使用
package main
import(
   "fmt"
)
type Integer int
func (a Integer) Less(b Integer)bool {
    return a<b
}
func main(){
   var a Integer=1
   var aa int=10
   a=Integer(aa)  //不強制轉換會編譯出錯的
   r:=a.Less(2)
   fmt.Println(r)
}
5、Go中的值語義和引用   
channel map本質是指針 因為復制他們沒有意義  ........數組切片 []type 本質上是值類型 interface接口非常重要
值語義和引用語義的差別在於賦值,比如下面的例子:
b = a
b.Modify()
如果b的修改不會影響a的值,那么此類型屬於值類型。如果會影響a的值,那么此類型是引用
類型。
Go語言中的大多數類型都基於值語義,包括:
 基本類型,如byte、int、bool、float32、float64和string等;
 復合類型,如數組(array) 、結構體(struct)和指針(pointer)等。
Go語言中類型的值語義表現得非常徹底。我們之所以這么說,是因為數組。
如果讀者之前學過C語言,就會知道C語言中的數組比較特別。通過函數傳遞一個數組的時
候基於引用語義, 但是在結構體中定義數組變量的時候基於值語義 (表現在為結構體賦值的時候,
該數組會被完整地復制) 。
Go語言中的數組和基本類型沒有區別,是很純粹的值類型,例如:
var a = [3]int{1, 2, 3}
var b = a
b[1]++
fmt.Println(a, b)
該程序的運行結果如下:
[1 2 3] [1 3 3]。
這表明b=a賦值語句是數組內容的完整復制。要想表達引用,需要用指針:
var a = [3]int{1, 2, 3}
var b = &a
b[1]++
fmt.Println(a, *b)
該程序的運行結果如下:
[1 3 3] [1 3 3]
這表明b=&a賦值語句是數組內容的引用。變量b的類型不是[3]int,而是*[3]int類型。
Go語言中有4個類型比較特別,看起來像引用類型,如下所示
 
數組切片:指向數組(array)的一個區間。
 map:極其常見的數據結構,提供鍵值查詢能力。
 channel:執行體(goroutine)間的通信設施。
 接口(interface) :對一組滿足某個契約的類型的抽象。
但是這並不影響我們將Go語言類型看做值語義。下面我們來看看這4個類型。
數組切片本質上是一個區間,你可以大致將[]T表示為:
type slice struct {
    first *T
    len int
    cap int
}
因為數組切片內部是指向數組的指針,所以可以改變所指向的數組元素並不奇怪。數組切片
類型本身的賦值仍然是值語義。
map本質上是一個字典指針,你可以大致將map[K]V表示為:
type Map_K_V struct {
 
// ...
}
 
type map[K]V struct {
    impl *Map_K_V
}
基於指針,我們完全可以自定義一個引用類型,如:
type IntegerRef struct {
   impl *int
}
channel和map類似,本質上是一個指針。將它們設計為引用類型而不是統一的值類型的原因
是,完整復制一個channel或map並不是常規需求。
同樣,接口具備引用語義,是因為內部維持了兩個指針,示意為:
type interface struct {
    data *void
    itab *Itab
}
接口在Go語言中的地位非常重要。關於接口的內部實現細節,在后面的高階話題中我們再
細細剖析。 
7、Go語言中的結構體  組合非繼承
Go語言的結構體(struct)和其他語言的類(class)有同等的地位,但Go語言放棄了包括繼
承在內的大量面向對象特性,只保留了組合(composition)這個最基礎的特性。
組合甚至不能算面向對象特性,因為在C語言這樣的過程式編程語言中,也有結構體,也有
組合。組合只是形成復合類型的基礎。
上面我們說到,所有的Go語言類型(指針類型除外)都可以有自己的方法。在這個背景下,
Go語言的結構體只是很普通的復合類型,平淡無奇。例如,我們要定義一個矩形類型:
type Rect struct {
    x, y float64
    width, height float64
}
然后我們定義成員方法Area()來計算矩形的面積:
func (r *Rect) Area() float64 {
    return r.width * r.height
}
可以看出, Go語言中結構體的使用方式與C語言並沒有明顯不同。 
8、Go中的組合精華  創建結構體指針 和為 結構體擴充成員函數的時候.....傳遞 值類型和指針類型的區別
package main
import "fmt"
type Rect struct{
    x,y float64
    width,height float64
}
///如果寫成rct Rect 那么內部的修改不會影響到 外部結構
///如果寫成rct*Rect那么內部的修改會影響到外部結構的值 這就是 指針的效果
func (rct *Rect)Area() float64{
     rct.width=1000 ///也可以(*rct).width=1000一樣
    return rct.width*rct.height
}
func main(){
     rct:=new(Rect)
//對於結構體指針,...調用方法和值類型一樣直接.唯一的區別是 作為參數傳遞的時候 傳遞的是地址 值可以被修改 
//所以進行組合的時候就有兩種選擇 
// 可以寫成 var rct*Rect=&Rect{}  
//也可以寫成 var rct Rect=Rect{}  
//var rct *Rect=new(Rect)  
//也可以寫成 var rct Rect=Rect{1,2,3,4}  
     rct.width=10.0
     rct.height=10.0
     area:=rct.Area()
     fmt.Println(area)
     fmt.Println(rct.width)
}
 
 
9、普通的組合繼承 ...........................以及組合指針繼承 以及覆蓋 和函數 成員名字沖突 
//通過值類型繼承
package main
import "fmt"
//Go中繼承屬於匿名組合 .......可以從對象繼承 也可以從指針匿名繼承...
//匿名繼承會去掉包名,,,所以不能同時繼承類名相同的  即使不在同一個包中
type Base struct{  
    Name string
    Age  uint8
}
///為Base結構體組合進去兩個函數
func (pBase*Base) showName(){
     fmt.Println("Age",pBase.Name)
}
func (pBase*Base) showAge(){
     fmt.Println("Age",pBase.Age)
}
//創建Sub結構體
type Sub struct{
    //組合Base修改內存模型
    //匿名組合進Base 對於調用者是不知道的
    //即使我們覆蓋了 Base的方法 但是我們還是可以通過xxx.Base.xxx()調用基類的方法的
   //如果是*Base我們需要在調用處手動添加new Base 否則運行會出錯的
    Base  
}
func (pSub*Sub) showName(){
  fmt.Println("Before Sub ShowName")
    pSub.Base.showName()
    fmt.Println("After Sub ShowName")
}
func main(){
      obj:=new(Sub)
      obj.Name="張三"
      obj.Age=15
      obj.showName()
      obj.showAge()
}
///通過指針類型繼承
package main
import "fmt"
//Go中繼承屬於匿名組合 .......可以從對象繼承 也可以從指針匿名繼承...
//匿名繼承會去掉包名,,,所以不能同時繼承類名相同的  即使不在同一個包中
type Base struct{  
    Name string
    Age  uint8
}
///為Base結構體組合進去兩個函數
func (pBase*Base) showName(){
     fmt.Println("Age",pBase.Name)
}
func (pBase*Base) showAge(){
     fmt.Println("Age",pBase.Age)
}
//創建Sub結構體
type Sub struct{
    //組合Base修改內存模型
    //匿名組合進Base 對於調用者是不知道的
    //即使我們覆蓋了 Base的方法 但是我們還是可以通過xxx.Base.xxx()調用基類的方法的
    *Base  
}
func (pSub*Sub) showName(){
  fmt.Println("Before Sub ShowName")
    pSub.Base.showName()
    fmt.Println("After Sub ShowName")
}
func main(){
      obj:=new(Sub)
    //由於使用指針繼承所以 我們要設置匿名組合模板的內存對象 地址
      obj.Base=&Base{}
      obj.Name="張三"
      obj.Age=15
      obj.showName()
      obj.showAge()
}
 
10、Go語言的可見性 權限是包一級的,包外的不能訪問包內的小寫開頭成員......包內無所謂
 
11、Go的非侵入式接口 和實現     
/////Go語言會為每一個成員函數 自動生成對應的函數  比如 func(a *A) 會自動生成 func (a A) .....
///反過來則不行 因為 func (a A)這時候傳遞的是形參  (&a).xx()改變的是 參數 副本 而不是 外部類
package main
import "fmt"
///非侵入式接口
////接口 和實現完全分析 減少耦合
///實現方只負責實現  接口方只負責封裝自己的借口就行...實現方甚至不知道 有這個接口的存在 這就是 Go的 非侵入式接口的特點
type IFly interface{
    fly()
}
type ISay interface{
    say()
}
type Bird struct{
     
}
//由於匿名傳遞進來的是指針類型 所對於接口的賦值必須是 指針
func (pBird*Bird) fly(){
   fmt.Println("i am a bird, i can fly()!")
}
//由於匿名傳遞的不是指針類型是值類型 所以接口賦值 可以不是指針而是值
func (pBird Bird) say(){
   fmt.Println("i am a bird, i can say()!")
}
func main(){
     birdObj:=Bird{}
     
     var iFly IFly=&birdObj
     iFly.fly()
     var iSay ISay=birdObj
     iSay.say()
}
13、接口之間是可以相互賦值的
實現了相同方法的接口可以相互賦值,如果接口B是A非超集,那么  A可以賦值為B
對象不可以被賦值為接口 ,繁殖接口可以被賦值為實現了 某些方法的對象 或者包含他方法的 接口對象
package main
import "fmt"
///非侵入式接口
////接口 和實現完全分析 減少耦合
///實現方只負責實現  接口方只負責封裝自己的借口就行...實現方甚至不知道 有這個接口的存在 這就是 Go的 非侵入式接口的特點
type IFly interface{
    fly()
}
type ISay interface{
    say()
}
type IFly1 interface{
    fly()
}
type ISay1 interface{
    say()
}
type Bird struct{
     
}
//由於匿名傳遞進來的是指針類型 所對於接口的賦值必須是 指針
func (pBird*Bird) fly(){
   fmt.Println("i am a bird, i can fly()!")
}
//由於匿名傳遞的不是指針類型是值類型 所以接口賦值 可以不是指針而是值
func (pBird Bird) say(){
   fmt.Println("i am a bird, i can say()!")
}
func main(){
     birdObj:=Bird{}
     
     var iFly IFly=&birdObj
     iFly.fly()
     var iSay ISay=birdObj
     iSay.say()
     ////接口之間的賦值
     var iFly1 IFly1=iFly
     iFly1.fly()
}
14、Go中的值類型非常的徹底  數組都是值類型
 
15、關於給類型添加String()方法   相當於 其他語言的toString 用於打印輸出
package main
import "fmt"
///非侵入式接口
////接口 和實現完全分析 減少耦合
///實現方只負責實現  接口方只負責封裝自己的借口就行...實現方甚至不知道 有這個接口的存在 這就是 Go的 非侵入式接口的特點
type IFly interface{
    fly()
}
type ISay interface{
    say()
}
type IFly1 interface{
    fly()
}
type ISay1 interface{
    say()
}
type Bird struct{
     
}
//由於匿名傳遞進來的是指針類型 所對於接口的賦值必須是 指針
func (pBird*Bird) fly(){
   fmt.Println("i am a bird, i can fly()!")
}
//由於匿名傳遞的不是指針類型是值類型 所以接口賦值 可以不是指針而是值
func (pBird Bird) say(){
   fmt.Println("i am a bird, i can say()!")
}
func (pBird Bird) String() string{
   return "aaaaaaaaaa"
}
func main(){
     birdObj:=Bird{}
     
     var iFly IFly=&birdObj
     iFly.fly()
     var iSay ISay=birdObj
     iSay.say()
     ////接口之間的賦值
     var iFly1 IFly1=iFly
     iFly1.fly()
     fmt.Println(birdObj)
}
16、接口的組合 就是把多個接口組合到一起......接口中只有函數沒有屬性
type IFly interface{
    fly()
}
type ISay interface{
    say()
}
type IFly1 interface{
    fly()
}
type ISay1 interface{
    say()
}
type ISay_Fly interface{
    ISay
    IFly
}
 
17、接口查詢 obj,ok=val.(Interface)   返回查詢的接口 並且返回查詢結果 
x.(type) 獲取類型 只能在switch中用 
x.(OterTypeInterface) 判斷x是否是指定接口類型 返回指定接口對象,和查詢結果
在Go語言中,還可以更加直截了當地詢問接口指向的對象實例的類型,例如:
var v1 interface{} = ...
switch v := v1.(type) {
    case int:    // 現在v的類型是int
    case string: // 現在v的類型是string 
...
}
就像現實生活中物種多得數不清一樣,語言中的類型也多得數不清,所以類型查詢並不經常使用。它更多是個補充,需要配合接口查詢使用,例如:
type Stringer interface {
    String() string
}
 
func Println(args ...interface{}) {
    for _, arg := range args {  
        switch v := v1.(type) {
            case int:                        // 現在v的類型是int
            case string:                     // 現在v的類型是string
            default:
            if v, ok := arg.(Stringer); ok { // 現在v的類型是Stringer
                val := v.String()
                // ...
            } else {
                // ...
            }
        }
    }
}
當然,Go語言標准庫的Println()比這個例子要復雜很多,我們這里只摘取其中的關鍵部分進行分析。對於內置類型,Println()采用窮舉法,將每個類型轉換為字符串進行打印。對於更一般的情況,首先確定該類型是否實現了String()方法,如果實現了,則用String()方法將其轉換為字符串進行打印。否則,Println()利用反射功能來遍歷對象的所有成員變量進行打印。是的,利用反射也可以進行類型查詢,詳情可參閱reflect.TypeOf()方法的相關文檔。此外,
18、 Any類型  對於匿名結構體賦值給任意類型  沒法取出 具體每個匿名結構體的內部屬性 只能前部打印 通過系統默認的String()函數
var any1 interface{}=1
var any2 interface{}="b"
var any3 interface{}=struct{x ,y string}{"hello,world","aaaaa"}
fmt.Println(any1,any2,any3)
由於Go語言中任何對象實例都滿足空接口interface{},所以interface{}看起來像是可以指向任何對象的Any類型,如下:
var v1 interface{} = 1       // 將int類型賦值給interface{}
var v2 interface{} = "abc"   // 將string類型賦值給interface{}
var v3 interface{} = &v2     // 將*interface{}類型賦值給interface{}
var v4 interface{} = struct{ X int }{1}
var v5 interface{} = &struct{ X int }{1}
當函數可以接受任意的對象實例時,我們會將其聲明為interface{},最典型的例子是標准庫fmt中PrintXXX系列的函數,例如:
func Printf(fmt string, args ...interface{})
func Println(args ...interface{})
...
總體來說,interface{}類似於COM中的IUnknown,我們剛開始對其一無所知,但可以通過接口查詢和類型查詢逐步了解它。
由於Go語言中任何對象實例都滿足空接口interface{},所以interface{}看起來像是可以指向任何對象的Any類型,如下:
var v1 interface{} = 1       // 將int類型賦值給interface{}
var v2 interface{} = "abc"   // 將string類型賦值給interface{}
var v3 interface{} = &v2     // 將*interface{}類型賦值給interface{}
var v4 interface{} = struct{ X int }{1}
var v5 interface{} = &struct{ X int }{1}
當函數可以接受任意的對象實例時,我們會將其聲明為interface{},最典型的例子是標准庫fmt中PrintXXX系列的函數,例如:
func Printf(fmt string, args ...interface{})
func Println(args ...interface{})
...
總體來說,interface{}類似於COM中的IUnknown,我們剛開始對其一無所知,但可以通過接口查詢和類型查詢逐步了解它。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM