Przesiadając się z Java na Go, chyba najtrudniej było mi się zaakceptować przysłowie: „Accept Interfaces, Return Concrete Types”.
Go interfaces generally belong in the package that uses values of the interface type, not the package that implements those values
Cytat za: https://github.com/golang/go/wiki/CodeReviewComments#interfaces
W Java’ie jest to normalne, że klasa interfejsu musi być znana po obu stronach. Dlatego też to dostarczający definiuje interfejs, a użytkownik go importuje i używa.
Go jest inne. Nie ma składni implements stąd interfejs dostarczającemu nie jest do niczego potrzebny. Może on zwrócić strukturę i już. Nawet prywatną. Użytkownik natomiast powinien oczekiwać interfejsu. Co powinien zrobić, jeżeli takiego interfejsu nie ma? Zdefiniować. Jeżeli tylko struktura będzie go implementować, można będzie użyć metody.
Przykład dostarczającego:
package db
type Store struct {
db *sql.DB
}
// NewDB initialise the DB
func NewDB() *Store { ... }
// Insert item
func (s *Store) Insert(item User) error { ... }
// Get item by id
func (s *Store) Get(id int) (User, error) { ... }
oraz użytkownik:
package user
type Saver interface {
Insert(user User) error
}
type Service struct {
Saver // Accepting interface here!
}
func (s Service) Register(f Form) error {
// ...
}
a także klej:
package app
import (
"example.org/user"
"example.org/db"
)
func Configure() {
us := user.Service{db.NewDB()}
// ...
}
Zupełnie płynnie. Jeżeli dostarczający doda jakieś metody do zwracanej struktury, użytkownik nic nie musi robić. Inaczej gdyby zwracał interfejs. Użytkownik będzie musiał zaimplementować nowe metody w testach.
Oczywistym jest też, że nie zawsze można taką regułę stosować. W przypadku gdy mamy kilka różnych implementacji, np. używając wzorca Strategy, będziemy musieli zwrócić interfejs.