🗓️ 02112024 2258
📎

INTERFACES

Core Concept: Interfaces define behavior contracts; types implement them implicitly (duck typing).

Why It Matters​

Enables polymorphism without inheritance. Foundation for dependency injection and testing.

When to Use​

✅ Use when:

  • Defining contracts
  • Dependency injection
  • Multiple implementations

❌ Don't use when:

  • Only one implementation
  • Premature abstraction

Key Principle​

Accept interfaces, return structs - Functions take interfaces as parameters but return concrete types.

This contrasts with type_assertions where you convert interfaces back to concrete types.

Trade-offs​

Pros: Loose coupling, testability, flexibility
Cons: Runtime overhead, less explicit

Quick Reference​

// Define interface
type Reader interface {
Read(p []byte) (n int, err error)
}

// Implement implicitly (no "implements" keyword)
type FileReader struct{}

func (f FileReader) Read(p []byte) (int, error) {
// implementation
}
// FileReader is now a Reader

// Use interface as parameter
func process(r Reader) {
r.Read(buf)
}

// Accept interfaces, return structs
func NewService(repo UserRepository) *Service { // ✅
return &Service{repo: repo}
}
ConceptJavaGo
Declarationclass X implements IImplicit
Empty interfaceObjectinterface{}
Type checkinstanceofType assertion

Examples​

Dependency injection without @Autowired:

type UserRepository interface {
FindByID(id int) (*User, error)
Save(user *User) error
}

type UserService struct {
repo UserRepository
}

func NewUserService(repo UserRepository) *UserService {
return &UserService{repo: repo}
}

// Both DBUserRepository and MockUserRepository satisfy
// UserRepository automatically — no "implements" keyword

Small interfaces principle — compose when needed:

type Reader interface {
Read(p []byte) (n int, err error)
}

type Writer interface {
Write(p []byte) (n int, err error)
}

type ReadWriter interface {
Reader
Writer
}

References​