Help learning interfaces

Hello, i’m trying to learn how to use interfaces (also learning c++, so it’s interesting to wrap my head around).

Suppose I have a struct like this:

type Book struct {
pages: int
title: string
author: string
}

And i have several other “people” like

type Consumer struct {
name: string
favoritebook: string
}

type Author struct {
name: string
authorID: int
favoritepublisher: string
}

type BookHater struct {
name: string
FavoriteTVShow: string
}

And I want consumer to be able to Read(), Author to Write(), and BookHater to ScreamAtSightOfBook(). But none can do any other function. How would this be possible? Should I create interfaces only for each “person”? Or is there a way to make an interface for “Book” and specify who can do what function?

1 Like

If Consumer remains the only type that can Read(), Author the only one that can Write() etc., you do not need any interface. Just implement the methods

func (c *Consumer) Read(...) ...
func (a *Author) Write(...) ...

etc. and you are done.

Only if you (or someone else) want to add a new type, say, Critic, that also shall be able to Read(), and a function that shall be able to call Read() on either a consumer or a critic, then the time has come to add an interface:

First, the critic:

func (c *Critic) Read(...) ...

Now the interface:

interface BookReader {
    Read(...)
}

And here is the function that makes any book reader read a book:

func letSomeoneRead(b BookReader) {
    b.Read()
}

Both Consumer and Critic implement BookReader, so you can pass both to letSomeoneRead():

func main() {
    co := Consumer{...}
    cr := Critic{...}
    letSomeoneRead(co)
    letSomeoneRead(cr)
}

The cool part here is that your original code does not have to change. Even if the original code is within some library that you cannot change, you still can add an interface in order to make functions like letSomeoneRead() possible.

3 Likes

Thank you christophberger! So this means that unlike C++ classes with inheritance, this will only group similar methods together?

Go replaces inheritance by two concepts: Interfaces and struct embedding.

Interfaces define a behavior. For example, the io.Reader interface defines reading from an (unknown) input stream into a byte slice. Many (very different) entities can implement this interface, and this makes it trivial to read from either a file, a network connection, or mabye even just a string - it is always the same call to Read().

(This is vaguely similar to an “is-a” relationship that C++ models through inheritance.)

Interfaces also enable writing functions with polymorphic parameters, like the letSomeoneRead() function from my previous comment.

Things like dependency injection also become almost trivial with interfaces.

Also worth noting is that interfaces in Go tend to be very small. Containing just one single function is not uncommon. Larger interfaces can always be composed of smaller ones. (Like ReadWriteCloser, which is composed of a Reader, a Writer, and a Closer.)

Embedding a type with methods into a struct allows code reuse. The struct can call the methods of the embedded type as if they were its own methods. This is the other part in Go that C++ would again use interfaces for.

Still, combining interfaces and embedding does not result in some sort of “Go-style inheritance”. Go simply has no inheritance (and avoids the problems that come with it), but I must admit it can take a while to become familiar with the alternative approaches that Go provides. Practicing helps a lot… :slight_smile:

2 Likes

Thanks for the explanation christophberger! I will have to make a small project for me now :slight_smile:

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