一樣都是使用接口,JAVA和Go差距咋就這麼大呢?

這篇文章將描述代碼中常用的搶佔式接口模式,以及爲何我認爲在Go中遵循這種模式一般是不正確的。java

什麼是搶佔式接口

接口是一種描述行爲的方式,存在於大多數類型語言中。搶佔式接口是指開發人員在實際須要出現以前對接口進行編碼。一個示例可能以下所示。程序員

type Auth interface {
  GetUser() (User, error)
}

type authImpl struct {
  // ...
}
func NewAuth() Auth {
  return &authImpl
}

搶佔式接口什麼時候有用

搶佔接口一般用於在Java中,而且大獲成功,這是大部分程序員的想法。相信,不少Go開發者也是這麼認爲的。這種用法主要區別在於Java具備顯式接口,而Go是隱式接口。讓咱們看一些示例Java代碼,這些代碼顯示了若是不使用Java中的搶佔式接口可能會出現的困難。函數

// auth.java
public class Auth {
  public boolean canAction() {
    // ...
  }
}
// logic.java
public class Logic {
  public void takeAction(Auth a) {
    // ...
  }
}

如今假設您要更改Logic的takeAction方法中的參數Auth類型的對象,只要它具備canAction()方法便可。不幸的是,你不能。Auth沒有在其中實現帶有canAction()的接口。你如今必須修改Auth爲其提供一個接口,而後您能夠在takeAction中接受該接口,或者將Auth包裝在一個除了實現的方法以外什麼都不作的類中。即便logic.java定義了一個Auth接口以在takeAction()中接受,也可能很難讓Auth實現該接口。您可能無權修改Auth,或者Auth可能位於第三方庫中。也許Auth的做者不一樣意你的修改。也許在代碼庫中與同事共享Auth,如今須要在修改以前達成共識。這是但願的Java代碼。編碼

// auth.java
public interface Auth {
  public boolean canAction()
}
// authimpl.java
class AuthImpl implements Auth {
}
// logic.java
public class Logic {
  public void takeAction(Auth a) {
    // ...
  }
}

若是Auth的做者最初編碼並返回一個接口,那麼你在嘗試擴展takeAction時,永遠不會遇到問題。它天然適用於任何Auth接口。在具備顯式接口的語言中,之後你會感謝過去的本身使用了搶佔式接口。code

爲何這在Go中不是問題

讓咱們在Go中設置相同的狀況。對象

// auth.go 
type Auth struct { 
// ... 
}
// logic.go 
func TakeAction(a *Auth) { 
  // ... 
}

若是logic想要使TakeAction通用,則logic全部者能夠單方面執行此操做,而不會打擾其餘人。接口

// logic.go 
type LogicAuth interface { 
  CanAction() bool 
}
func TakeAction(a LogicAuth) { 
  // ... 
}

請注意 auth.go 不須要更改。這是使搶佔式接口再也不須要的關鍵所在。開發

Go中搶佔式接口的意外反作用

Go的接口定義都是很小,但很強大。在標準庫中,大多數接口定義都是單一方法。這容許最大的重用,由於實現接口很容易。當程序員對像上面的Auth這樣的搶佔式接口進行編碼時,接口的方法數量每每會激增,這使得接口(可交換實現)的所有意義更難以實現。io

Go中接口的最佳用法

Go的一個很好的經驗法則是-接受接口,返回結構體。接受接口爲您的API提供了最大的靈活性,返回結構體容許調用者快速導航到正確的函數。class

即便你的Go代碼接受結構體並返回結構體以啓動,隱式接口也容許您稍後擴展你的API,而不會破壞向後兼容性。接口是一種抽象,抽象有時頗有用。然而,沒必要要的抽象會形成沒必要要的複雜化。在須要以前不要使代碼過於複雜。

相關文章
相關標籤/搜索