If I understand Go practices correctly, callers (aka consumers) are supposed to define interfaces of what they want to use from their dependencies (aka producers).
However, if the producer has a function that accepts a custom type, then it's better to make it accept an interface, right? This way a consumer could just pass some value that complies with producer's interface, without knowing the exact type. Because an input value into a producer's function makes the producer become the "consumer" of that input value.
Okay, fair enough.
The question is, how can consumer define an interface, which contains a function, whose parameter is an interface defined in the producer?
Trying to make the question clearer
Let's say I have a package called chef
which has a struct Chef
. It has a method Cut(fruit) error
and fruit
is an interface defined in my chef
package.
Now let's say I am in the calling code, and I import package chef
. I want to give it a fruit to cut, but in my case, I implemented a specific fruit called Apple
. Naturally, I will try to build this interface for myself:
type myRequirements interface {
Cut(Apple) error
}
Because I have the specific implementation of fruit
interface called Apple
, I want to indicate that my interface just works with apple.
However, if I try to use Chef{}
against my interface, Go will throw a compile error, because my interface wants to Cut(Apple)
and the Chef{}
wants to Cut(Fruit)
. This is despite the fact that Apple implements fruit
.
The only way to avoid this, it seems, is to make chef.Fruit
a public interface, and use that in my own interface.
type myRequirements interface {
Cut(chef.Fruit) error
}
But this completely ruins my ability to plug a different implementation (instead of chef
) under my interface, because now I'm tightly coupled to chef
.
So Chef has an internal interface fruit
, but caller only knows about Apple. How can I indicate in the caller's interface what input should go into Cut
without referencing chef
?
Answering a comment "Why do you need myRequirements?"
I was surprised that this isn't a more agreed upon concept in the Go community.
The reason I need a myRequirements interface is because I’m a consumer of chef package. Besides Cut
, chef may have 100 more methods. But I only use Cut
. I want to indicate to other developers, that in my situation I’m only using Cut
. I also want to allow tests to only mock Cut
for my code to work. Additionally, I need to be able to plug a different implementation of Cut
(from a different chef). This is a golang best practice as alluded to in the beginning of my post.
Some quotes as evidence:
Golang Wiki says: "Go interfaces generally belong in the package that uses values of the interface type, not the package that implements those values."
Dave Cheney's blog explains: "Interfaces declare the behaviour the caller requires not the behaviour the type will provide. Let callers define an interface that describes the behaviour they expect. The interface belongs to them, the consumer, not you."
Jason Moiron's tweet points out a common misunderstanding: "people have it backwards: #golang interfaces exist for the functions that use them, not to describe the types that implement them"
Update
The best advice I got so far is to move the interface into a 3rd package, independent of caller and producer. For example, make a kitchen
package, define Fruit
interface in it, and use it in both chefs and callers. Kind of like everyone uses time.Time
. Perhaps that's the best advice. That said, I would still like to get an authoritative perspective from someone who tried to deal with this problem in their real work.