← Back to list

Golang: Struct, Interface And Dependency Injection(DI)

Write Clean, Flexible, and Testable Go Code with Structs and Interfaces!

Nidhi D in Canopas · 2025-01-17 07:11 · 261 claps · 2.9 min read
#golang #interfaces #struct #dependencies #clean-code
Open on Medium ↗

Golang: Struct, Interface And Dependency Injection(DI)

Write Clean, Flexible, and Testable Go Code with Structs and Interfaces!

Designed By Canopas

Designed By Canopas

Exciting News! Our blog has a new home! 🚀

Background

In this blog, we will explore when to use structs versus interfaces in Go and how to leverage both for Dependency Injection (DI).

To make these concepts easier to grasp, we’ll use a simple analogy of a Toy Box.

Understanding with a real-world example: Toy Box

Structs

  • Think of a struct as a specific toy in a toy box, like a car.
  • The car has specific features, like its color, size, and type (e.g., sports car).
  • In programming, a struct holds data about an object.

Interfaces

  • An interface is like a toy box that can hold any type of toy.
  • It defines what toys can do, like roll, make noise, or light up. Any toy that can perform these actions can fit in the toy box.
  • In programming, an interface defines a set of methods that different types(struct) can implement.

Dependency Injection

  • Imagine a child who plays with toys. Instead of the child only being able to play with one specific toy, you let them choose any toy from the toy box whenever they want.
  • This is like dependency injection, where you provide a function or class with the tools (or dependencies) it needs to work, allowing for flexibility.

Understanding the Basics

Structs

  • Definition: A struct is a way to define a new type with specific fields.
  • Purpose: Useful for modeling data structures and encapsulating data and behavior within a single unit.

Example,

type Car struct {
    Model string
    Year  int
}

Interfaces

  • Definition: An interface defines a set of methods that a type must implement.
  • Purpose: Crucial for polymorphism and decoupling components, enabling generic programming.

Example,

type CarInterface interface {
    Start()
    Stop()
}

Implement CarInterface using Car struct,

func (c *Car) Start() {
    fmt.Println("Car started")
}

func (c *Car) Stop() {
    fmt.Println("Car stopped")
}

When to Use Which?

Use Structs When,

  • You need to model a specific data structure with defined fields.
  • You want to encapsulate data and behavior within a single unit.

Use Interfaces When,

  • You want to define a contract that multiple types can implement.
  • You need to decouple components and make your code more flexible and testable.
  • You want to leverage polymorphism to write generic code.

Balancing Flexibility and Performance

While interfaces provide flexibility, dynamic method calls can introduce overhead.

Structs, on the other hand, offer performance advantages due to static type checking and direct method calls. Below are the ways to strike the balance:

Interface Composition

Combine multiple interfaces to create more specific interfaces. For example, consider a file system interface:

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

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

Now, we can create a more specific interface ReadWrite, by composing Reader and Writer:

type ReadWrite interface {
    Reader
    Writer
}

Benefit: This approach promotes modularity, reusability, and flexibility in your code.

Interface Embedding

Embed interfaces within structs to inherit their methods. For example, consider a logging interface:

type Logger interface {
    Log(message string)
}

Now, we can create a more specific interface, ErrorLogger, which embeds the Logger interface:

type ErrorLogger interface {
    Logger
    LogError(err error)
}

Any type that implements the ErrorLogger interface must also implement the Log method inherited from the embedded Logger interface.

type ConsoleLogger struct{}

func (cl *ConsoleLogger) Log(message string) {
    fmt.Println(message)
}

func (cl *ConsoleLogger) LogError(err error) {
    fmt.Println("Error:", err)
}

Benefit: This can be used to create hierarchical relationships between interfaces, making your code more concise and expressive.

This blog post was originally published on **canopas.com.**

To read the full version, please visit **this blog.**

Similar Articles

[embed]How to use Enums in Golang Exploring ways of creating Enums using iota identifier and struct tagsmedium.com

[embed]Function Types and Higher-Order Functions in Go One of the ways to make your code more readable and effective…medium.com

Thanks for the love you’re showing!

If you like what you read, be sure you won’t miss a chance to give 👏 👏👏 below — as a writer it means the world!

Feedback and suggestions are most welcome, add them in the comments section.

Follow **Canopas **to get updates on interesting articles!


메타데이터
post_id
08f8ffddf87d
slug
golang-struct-interface-and-dependency-injection-di-08f8ffddf87d
url
https://medium.com/canopas/golang-struct-interface-and-dependency-injection-di-08f8ffddf87d
canonical_url
https://medium.com/canopas/golang-struct-interface-and-dependency-injection-di-08f8ffddf87d
author_url
https://medium.com/@cp-nidhi-d
status
ok
fetched_at
2026-06-10 08:17:25