When should I exactly use interface?

I understand how interface works but when it comes to implement it I am having hard time understanding when should I exactly need it?

For example using struct is very clear:

type Repo struct {
  Db *gorm.DB
}
func (repo Repo) MyFunc() {}
func (repo Repo) MyAnotherFunc() {}

We can define method on struct and use them:

r := Repo{Db: &gorm.DB}
r.MyFunc()
r.MyAnotherFunc()

But when it comes to interface my head turns around:

type Repo struct {
  srv SomeInterface
}

Let’s suppose SomeInterface has MyFunc and MyAnotherFunc signatures. And so, we can use:

r := &Repo{srv: &gorm.DB}
func (repo Repo) MyFunc() {}
func (repo Repo) MyAnotherFunc() {}

But why? Why exactly we need this and what struct implementation is not enough and we need interface? Can you please give me a good example demo so that I can understand it properly. Thanks.

Interfaces are great if you have different implementations. For example:

type Repo interface {
    MyFunc()
    MyAnotherFunc()
}

type SQLRepo struct {
    Db *gorm.DB
}

func (repo SQLRepo) MyFunc() {}
func (repo SQLRepo) MyAnotherFunc() {}

type MongoDBRepo struct {
    client *mongo.Client
}

func (repo MongoRepo) MyFunc() {}
func (repo MongoRepo) MyAnotherFunc() {}

func NewRepo(connectionString string) (Repo, error) {
    switch {
    case strings.HasPrefix(connectionString, "mongodb://"):
        return &MongoRepo{mongodb.Connect(/* ... */)}, nil
    case strings.HasPrefix(connectionString, "sql://"):
        return &SQLRepo{/* ... */}, nil
    }
    return nil, fmt.Errorf("implementation cannot be determined by connectionString")
}

func main() {
    // parse arguments or a config file or something
    repo, err := NewRepo(config.ConnectionString)
    // now you can use repo and not care if it's backed by SQL
    // or MongoDB, etc.
}
1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.