Design Go interfaces by accepting them as function parameters and returning concrete structs as results to maximize flexibility and minimize coupling. Define an interface that describes only the behavior needed by the caller, then implement that interface with a struct that holds the actual state.
// Define interface with only required methods
type Reader interface {
Read(p []byte) (n int, err error)
}
// Function accepts interface (flexible input)
func Process(r Reader) error {
var buf [1024]byte
_, err := r.Read(buf[:])
return err
}
// Function returns concrete struct (explicit output)
func NewFileReader(name string) *FileReader {
return &FileReader{name: name}
}
type FileReader struct {
name string
}
func (f *FileReader) Read(p []byte) (int, error) {
// Implementation details
return 0, nil
}