2017-07-19 27 views
2

在Effective Go中,它是clearly stated,在字段未導出的情況下(以小寫字母開頭),getter方法應具有相同的字段名稱,但應以大寫字母開頭;他們給出的例子是作爲字段的owner和作爲方法的Owner。他們明確建議不要在獲取方法名稱前面使用GetGo Getter方法vs字段,正確命名

我經常遇到需要將字段導出爲JSON封送,與ORM一起使用或與其他反射相關的目的(IIRC反射可以讀取但不能修改未導出的字段)的情況,所以我的字段需要被調用在上面的例子中爲Owner,因此不能有一個Owner方法。

有沒有一種習慣的命名方式來解決這種情況?

編輯:這裏是我遇到的一個具體的例子:

type User struct { 
    Username string `db:"username" json:"username"` 
    // ... 
} 

// code in this package needs to do json.Unmarshal(b, &user), etc. 

// BUT, I want code in other packages to isolate themselves from 
// the specifics of the User struct - they don't know how it's 
// implemented, only that it has a Username field. i.e. 
package somethingelse 

type User interface { 
    Username() string 
} 

// the rest of the code in this other package works against the 
// interface and is not aware of the struct directly - by design, 
// because it's important that it can be changed out without 
// affecting this code 
+1

只需使用「獲取」前綴即可。這就是[Go protobuf generator](https://github.com/golang/protobuf)所做的。 –

+0

@TimCooper我聽到你的聲音,但我幾乎寧願打亂字段名稱,因爲接口方法名稱會出現在更多的地方(並在更多的包中使用),對於直觀和習慣用法來說更重要。 –

+4

如果導出字段,爲什麼要使用Getter/Setter?這只是讓界面更容易混淆,通過允許多種方式來完成同樣的事情。 – Flimzy

回答

2

如果您的字段被導出,請不要使用getter和setter。這只是混淆了界面。

如果您需要一個getter或setter,因爲它執行某些操作(驗證,格式化等),或者因爲您需要該結構來滿足接口,那麼不要導出基礎字段!您需要一個getter/setter,然後使用兩個結構體,一個導出和一個私有,並定義一個自定義的JSON編組器(或者是一個自定義的JSON編組器),或者定義一個自定義的JSON編組器在公共的那個DB訪問方法):

type jsonFoo struct { 
    Owner string `json:"owner"` 
} 

type Foo struct { 
    owner string 
} 

func (f *Foo) SetOwner(username string) { 
    // validate/format username 
    f.owner = username 
} 

func (f *Foo) Owner() string { 
    return f.owner 
} 

func (f *Foo) MarshalJSON() ([]byte, error) { 
    return json.Marshal(jsonFoo{ 
     Owner: f.owner, 
    }) 
} 
+0

我明白了,這是可行的,但我想避免。請參閱我的OP編輯,瞭解我正在嘗試做什麼的更多信息。 –

+1

有趣 - 你說得對,使用兩個單獨的結構可能不會太糟糕。這是一個處理編組而不用自定義代碼的變體 - 具有字段的結構嵌入到具有以下方法的外部結構中:https://play.golang.org/p/l_-PRmmydG –

+1

使用接口是使用這兩個結構的一個很好的理由。不過,你仍然應該使用這兩個結構,因爲同時擁有getter/setter和導出的字段仍然是令人困惑和糟糕的設計。 – Flimzy

0

我要張貼此作爲一個答案,考慮@ Flimzy的答案,這讓很多的意義後得出。

其基本思想是將一個結構與導出字段用於封送處理,另一個單獨結構的唯一目的是提供滿足所需接口的方法。

這不需要自定義編組代碼,並給出了一個明確的含義,以每結構,IMO:

type user struct { 
    Username string `json:"username"` 
} 

type ExportedUser struct { 
    // EDIT: explicitly doing "user *user" instead of just doing "*user" 
    // helps avoid confusion between between field names and methods 
    user *user 
} 

func (u ExportedUser) Username() string { return u.user.Username } 

func main() { 
    fmt.Printf("Test: %q", ExportedUser{user: &user{Username: "joe"}}.Username()) 
} 

它是否應該user以上User的問題是有點不清楚,我 - 因爲它可能對於在Go模板中可見的類型是有意義的,例如{{euser.User.SomeOtherField}}因此,如果需要,允許所有字段都可訪問。無論如何,上述答案的功能都是一樣的。

+0

該解決方案仍然導出嵌入的'用戶名'字段。試試這個:'x:= ExportedUser {user:user {用戶名:「joe」}}; fmt.Println(x.Username)'。你可以通過將'* user'命名爲'ExportedUser'中的一個字段來解決這個問題,而不是像'type ExportedUser struct {user * user}'中那樣嵌入它' – Flimzy

+0

@Flimzy有效點 - 儘管我的例子中的代碼編譯了,如果用戶只有字段,而且ExportedUser只有方法,並且排除Go的語法糖(將u.FieldName轉換爲u.embeddedStruct.FieldName)的任何可能性都會導致混淆,那麼將會更加清楚明確。 –

+1

當然,你的代碼是有效的,所以應該編譯。它只是不能解決通過getter/setter模式和導出變量暴露相同數據的問題。但我認爲你瞭解這些影響,所以現在你可以做出你的決定:) – Flimzy