Accepting Dependencies as Interfaces and Returning Concrete Types

Prabesh
2 min readJun 28, 2024

--

You’re working on a complex system, and suddenly, making changes or adding features becomes a nightmare because everything is tightly coupled. Sound familiar?

One crucial best practice to tackle this issue is accepting dependencies as interfaces in constructors and returning concrete types. Let’s dive into why this is important and the benefits it brings.

Scenario:

Imagine you have a service that depends on a repository. If your service directly instantiates the repository, your code becomes tightly coupled, making it hard to test, extend, or replace the repository without touching the service.

Why It’s Important:

  1. Decoupling: By accepting dependencies as interfaces, you decouple your classes from specific implementations, leading to more modular and flexible code.
  2. Testability: It becomes easier to mock dependencies in unit tests, allowing for more comprehensive and isolated testing.
  3. Extensibility: You can easily extend or replace implementations without modifying the dependent class, facilitating easier maintenance and feature updates.

Benefits:

  • Increased Flexibility: Your code becomes adaptable to changes. You can switch out implementations without refactoring the dependent classes.
  • Enhanced Testability: Writing unit tests becomes straightforward since you can mock interfaces instead of dealing with concrete classes.
  • Better Design: Encourages following SOLID principles, particularly the Dependency Inversion Principle, leading to a cleaner and more maintainable codebase.

Here a example snippet

// Define the interface
type Repository interface {
GetData(id string) (Data, error)
}

// Service accepts the interface
type Service struct {
repo Repository
}

func NewService(repo Repository) *Service {
return &Service{repo: repo}
}

// Concrete implementation
type ConcreteRepository struct{}

func (r *ConcreteRepository) GetData(id string) (Data, error) {
// Implementation
}

By following this pattern, you make your code more resilient to change, easier to test, and more maintainable in the long run.

--

--

Prabesh

Senior Site Reliability Engineer & Backend Engineer | Docker Captain 🐳 | Auth0 Ambassador @Okta | https://www.linkedin.com/in/prabeshthapa