Interfaces pequenas + composition
- ⬜⏱️ Context package: cancellation, timeout, values(Go Profissional)
Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.
"The bigger the interface, the weaker the abstraction"
| Tamanho da interface | Quem consegue implementar | Custo no teste | Efeito prático |
|---|---|---|---|
| 1 método | Praticamente qualquer coisa, inclusive uma função adaptada | Struct de cinco linhas escrita à mão | A abstração é forte porque exige pouco |
| 3 a 4 métodos | Tipos desenhados para aquele papel | Duplo ainda viável à mão | Razoável quando os métodos são coesos |
| 10+ métodos | Só a implementação real | Precisa de ferramenta de geração de mock | A ferramenta existe para compensar a abstração ruim |
| Interface do próprio pacote que a implementa | Um único tipo | Nenhum ganho | Sinal de que a interface foi criada por hábito, não por necessidade |
Rob Pike resume Go numa frase. Interface grande fica impossível de satisfazer fora da implementação original. Interface pequena (1–3 métodos) é reaproveitável, testável e composável. A stdlib é exemplo permanente: io.Reader, io.Writer, io.Closer, fmt.Stringer, error — todos com uma ou duas operações.
Interfaces da stdlib que valem memorizar
type Reader interface { Read(p []byte) (n int, err error) }
type Writer interface { Write(p []byte) (n int, err error) }
type Closer interface { Close() error }
type Stringer interface { String() string }
// Composição por embedding
type ReadCloser interface {
Reader
Closer
}Accept interfaces, return structs
// Bom: recebe contrato mínimo, retorna tipo rico
func Copy(src io.Reader, dst io.Writer) (int64, error) { ... }
type Server struct { ... }
func NewServer(cfg Config) *Server { return &Server{...} }
// Ruim: obriga caller a satisfazer contrato grande pra chamar
func Copy(src LocalFileReader, dst LocalFileWriter) { ... }
// E: retornar interface empobrece API
func NewServer(cfg Config) ServerLike { ... } // anti-padrãoAo retornar *Server, você expõe métodos concretos (Shutdown, SetTimeout). Se retornasse uma interface, cada método extra exigiria uma nova versão do contrato.
Composition via embedding
type Logger struct { level int }
func (l *Logger) Info(msg string) { ... }
func (l *Logger) Error(msg string) { ... }
type Server struct {
*Logger // embed: métodos promovidos
addr string
}
s := &Server{Logger: &Logger{level: 1}, addr: ":8080"}
s.Info("starting") // chamada direta, sem delegação manual"Aceite interfaces, retorne structs". Por que retornar a interface piora a situação de quem escreve a biblioteca?
Mock via interface pequena
type UserStore interface {
Get(ctx context.Context, id string) (User, error)
}
// Produção
type pgStore struct { db *sql.DB }
func (s *pgStore) Get(ctx context.Context, id string) (User, error) { ... }
// Teste
type fakeStore struct { users map[string]User }
func (f *fakeStore) Get(_ context.Context, id string) (User, error) {
u, ok := f.users[id]
if !ok { return User{}, ErrNotFound }
return u, nil
}Uma interface de um método substitui toda a ginástica de mock framework. Teste fica rápido, claro e sem dependência externa.
Armadilha: interface nil vs ponteiro nil
var perr *MyError = nil
var err error = perr // err tem (tipo=*MyError, valor=nil)
fmt.Println(err == nil) // false! <<< bug
// Correto:
func op() error {
if someCondition {
return &MyError{...}
}
return nil // nil explícito, não variável typed
}Esse é o tropeço número 1 de quem chega em Go. A regra segura: no sucesso, return nil literal. Nunca retorne variável de ponteiro typed supostamente nula.
Perguntas frequentes
❓ Por que interface pequena é melhor?
❓ Onde declarar a interface?
❓ Devo criar interface para tudo?
Fixando
`var err error = perr`, com `perr` sendo um ponteiro nulo, faz `err == nil` devolver falso. Por quê?
Qual é a vantagem prática de uma interface de um único método na hora de testar?
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…