這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。## 注意細節在之前的文章中,我提到了一個關於 *accept interfaces, return structs* 的參考指南,在查看同事代碼的時候經常會被問“為什麼”。特別是這不是一個必須遵守的規則。這個想法的關鍵點以及理解什麼時候妥協,在於維護項目靈活性和避免搶佔抽象(譯者註:“Preemptive abstractions” 並發系統中連續組件的輕量級驗證方案的一種抽象技術)之間的平衡。## 搶佔抽象讓系統變得複雜> 除了因為太多的迂迴方式所造成的問題之外,所有的電腦科學問題都能夠通過另一個層級的迂迴方式來解決。 > - David J. Wheeler軟體工程師喜歡抽象。個人看法,我從未看到過一個同事參與寫代碼超過他為了某個事務建立抽象多。Go 語言從結構中抽象出介面,這種處理方式會產生嵌入複雜性。遵循[你並不需要它](http://c2.com/xp/YouArentGonnaNeedIt.html)軟體設計理念,如果不需要就沒有理由增加複雜性。一個常見的返回介面的理由是讓使用者把注意力放在函數所提供的 API 上。在 Go 中因為隱含實現了介面,所以這並不需要。返回結構的公用函數就成為那個API。> 永遠只有當你真正需要的時候才抽象,不要因為預見可能會需要而抽象一些語言需要你預見每一個可能從未用過的介面。隱含實現介面一個最大的好處,就是允許你在後面實際需要的時候優雅的抽象事務,而不是需要你預先抽象出來。## 使用者眼中的需求> 當你真正需要他們的時候你怎麼知道什麼時候需要抽象?對於傳回型別來說,比較容易。你是寫函數的人,所以你確切的知道什麼時候需要抽象傳回值。 對於輸入參數來說,是否需要不在你的控制範圍之內。你也許認為你的資料模型足夠了,但是一個使用者可能需要和某些屬性封裝一下。如果可能的話,可以預想一下每個調用你的函數的情況,但這是比較困難的。這種可以控制輸出,但是不能預期使用者輸入的不平衡的狀況產生了一種強烈的偏見,抽象輸入而不是輸出。## 去掉無用的代碼細節複雜的做雞蛋的方法簡化的另一個方面是去除不需要的細節。類似菜單函數:給一個輸入然後你得到一個蛋糕!不需要列出做蛋糕的材料。同樣的,函數也不需要列出不使用的輸入參數。下面的函數你會怎麼想? ```gofunc addNumbers(a int, b int, s string) int {return a + b}```對於大多數程式員來說很明顯參數 **s** 是不需要的。當參數是結構的時候就不那麼明顯了。```gotype Database struct{ }func (d *Database) AddUser(s string) {...}func (d *Database) RemoveUser(s string) {...}func NewUser(d *Database, firstName string, lastName string) {d.AddUser(firstName + lastName)}```就像一個寫滿了配料的菜單一樣,NewUser 輸入參數是一個有很多功能的 Database 對象。實際上只需要 AddUser 但是卻得到了額外的 RemoveUser。介面允許我們在建立函數的時候只依賴我們需要的功能。```gotype DatabaseWriter interface {AddUser(string)}func NewUser(d DatabaseWriter, firstName string, lastName string) {d.AddUser(firstName + lastName)}```Dave Cheney 在寫[介面隔離原則](https://en.wikipedia.org/wiki/Interface_segregation_principle)的時候也提到了[這一點](https://dave.cheney.net/2016/08/20/solid-go-design)。他還描述了一些關於限制輸入的其他好處,值得讀一下。概括一下就是: > 依照需求描述的結果也就是函數-僅僅是需要可寫並且提供相應的功能我會按照這個思想,重新考慮上面的函數 addNumbers,很明顯不需要參數 s 字串,函數 NewUser 同樣也不需要一個包括 RemoveUser 的Database參數。## 總結理由和審查例外情況主要的理由如下:- 移除不需要的抽象- 在函數參數上使用者需求的歧義- 簡化函數參數這些理由也允許有例外的情況。例如,如果你的函數實際上返回多種類型而不是一個介面。同樣地,如果函數是私人的,你能控制輸入參數,會偏向於不要做搶佔抽象。對於第三條規則,go 沒有方式可以抽象出結構成員的值。 所以,如果你的函數需要訪問結構成員(並且不只是結構方法),那麼你必須接受結構作為參數。## 問答提問:如果 Database 有很多方法,比如 20 個,那麼如何處理呢?回答:一個結構可能有 20 個方法,但是一個方法不需要調用那麼多方法。 試著把這些方法按組分類,比如讀的方法,寫的方法,管理的方法等等。這樣需要 Database 的函數可以使用方法子集來處理。
via: https://medium.com/@cep21/what-accept-interfaces-return-structs-means-in-go-2fe879e25ee8
作者:Jack Lindamood 譯者:tyler2018 校對:polaris1119
本文由 GCTT 原創編譯,Go語言中文網 榮譽推出
本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻嗎?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在本文中標註並保留原文/譯文連結和作者/譯者等資訊。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽
434 次點擊