簡體   English   中英

使用 golang 嵌套結構(帶接口)

[英]Using golang nesting structs (with interfaces)

簡而言之,對於一個好的架構,我們應該使用接口,這對任何人來說都不是秘密,接口描述了它的行為。 Golang 實現了這個想法,但是他們的接口只有方法,沒有字段。 因此,使用它們的唯一方法就像創建 getter 和 setter。 但是,我遇到了指針問題。

例如:


package main

import "fmt"

// A

type IA interface {
    Foo() string
}

type A struct {
    foo string
}

func (a *A) Foo() string {
    return a.foo
}

// B

type IB interface {
    A() *IA
}

type B struct {
    a *IA
}

func (b *B) A() *IA {
    return b.a
}

// main

func main() {
    a := &A{"lol"}
    b := &B{a} // cannot use a (type *A) as type *IA in field value: *IA is pointer to interface, not interface

    foo := b.A().Foo() // b.A().Foo undefined (type *IA is pointer to interface, not interface)
    fmt.Println(foo)
}


Ofc,我可以使用這樣的東西:

(*(*b).A()).Foo()

但這會那么好和合適嗎?

我只想擁有像 python、js、ts 這樣的行為:

someObj.child1.child2.child2SomeMethod()

也許我搞砸了指針,我只想知道 golang 處理嵌套對象的方式。

這是一個經常被絆倒的地方,特別是對於那些剛接觸高級語言背景的人。 這是我保持直率的方式:

首先,讓我們確定 Go 中的“接收器”(例如“方法”)是什么。 很像python,方法實際上連接到類型的事實是語法糖。 考慮這個例子:

package main

import "fmt"

type F struct {
  i int
}

func (f F)Foo() {
  fmt.Println(f.i)
}

func main() {
  F.Foo(F{1})
}

令人驚訝的是,此代碼編譯並成功打印出預期的1 那是因為當你在一個類型上調用一個接收器時,真正發生的是該類型成為接收器的第一個參數。

真的很快,讓我們也回顧一下指針,因為您似乎在評論中倒退了。 所以要明確一點:計算機程序中的任何都存儲在內存中,它在內存中的地址也是可以存儲在變量中的值。 我們稱這些變量為“指向”值的指針。

如果我向函數傳遞一個,則他們只能在其函數范圍內更改該值。 如果該值是內存中的地址,則同樣如此。 但是,我可以更改該地址處的數據,從而影響具有函數范圍之外的數據。

package main
import "fmt"

func f(i int) { i = i + 2 }

func pf(i *int) { *i = *i + 2 }

var i = 1

func main() {
  f(i)
  fmt.Println(i)
  pf(&i)
  fmt.Println(i)
}

打印出來

1
3

f(i)改變其本地副本i ,但pf(&i)在存儲在地址改變數據i

為什么我要經歷這一切? 因為這就是 go 中大多數接收器都是指針接收器的原因; 因為你不想傳遞接收者的副本 您實際上想要傳遞接收者的地址,以便它可以在自己的方法中改變自己。

請記住,接收器是語法糖:

func (t *Type)f(arg string)

相當於:

func f(t *Type, arg string)

希望這能說明為什么指針接收器如此普遍! 好的,進入界面方面。

如您所知,接口定義了一組方法。 如果一個類型定義了具有相同簽名的方法,則該類型滿足該接口。 這對於值接收器指針接收器可能是正確的。

但是,一個類型不能同時具有同名的值和指針接收器:

func (t  T)F() {}
func (t *T)F() {} //method redeclared: T.F

所以這意味着一個類型和一個指向該類型的指針不能有相同的接收者; 因此,類型或指向該類型的指針都實現了接收器,但不能同時實現兩者 這一點很容易被忽略,因為 go 會自動轉換。 所以這很好用:


type T struct{}

func (t  T)F() {}
func (t *T)PF() {}

func main() {
 var t T 
 t.F()
 t.PF()
}

t.PF()t會自動轉換為指針。

但是重申一下,類型和指向類型的指針不能同時定義相同的接收者 所以如果T滿足接口I*T不滿足,反之亦然。

已經說過和理解了,很容易提出一個簡單的規則:當您打算使用指針滿足接口時,永遠不要使用指向接口的指針

在您的代碼中, *A滿足IA 所以你可以說你的IA是“真的是一個 *A . Thinking of it like this, you can see that taking the address of an IA that is actually an *A` . Thinking of it like this, you can see that taking the address of an IA that is actually an沒有任何意義。

綜上所述,這里定義了兩個接口並鏈接調用。 請注意,雖然指向我的結構的指針可能是滿足接口的值,但我從不需要獲取接口的地址。 對於IFIG接口的消費者來說,它們是類型還是指針接收器都無關緊要。

package main

import "fmt"

type IF interface {
  G() IG
}

type F struct {
  g IG
}

func (f *F)G() IG {
  return f.g
}

type IG interface {
  G()
}

type G struct{
  i int
}

func (g *G)G() {
  g.i++
  fmt.Println("Gee, ", g.i)
}

func main() {
  f := F{&G{1}}
  f.G().G()
}

需要指向接口的指針是非常罕見的,因此請確保您認為“指針滿足接口”而不是“指向接口的指針”。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM