Go Interfaces and Delegation Pattern
Pan Chasinga
If Rumi were a programmer. Open-source, computer art, education. Author of RxGo.
First published on Medium.
If you’re coding in Go but have not yet dabbled into interfaces, you’re missing out. Interfaces are core to the Go language, and they can be used to organize your code to prevent import cycles.
Note: If you already know interfaces, skip down to Broker and Delegate Pattern.
Interfaces in Go
Interfaces are not classes. They exist in language like Java to aid the lack of multiple inheritance (which is a good lack).
There’s a sound amount of counter-movement to class-based OOP nowadays. If you have worked with classes in any language, you might have felt some redundancy. Classes can be viewed as overkills or an inside-out view from the development perspective (and also a Darwinian one). A new and refreshing outside-in view is composition.
But I’m here to write about interfaces, specifically in Go. I assume you have used inheritance in Go with embedding in structs:
type structtype struct
This is a composition pattern?—?a pattern in which one identity is in a IS-A or HAS-A relationship with another by acquiring the properties or the identities of it. Here we add some favorite to the cat and it will return a sound as a response.
funcappend
return
But wait! A dog, a reindeer, or a sloth should be able to be given a favorite food and respond with gratitude too! The Cat is a bourgeois prick, as it always were!
It turns out that Animal itself isn’t a being?—?it is a category or a concept. In this case we might use an interface instead of a struct.
Have you ever heard, “If it walks and swims like a duck, it is probably one” or the famous quote from Deng Xiaoping, “It doesn’t matter whether a cat is white or black, as long as it catches mice.”?
Well, it does make sense to categorize something by the abilities to do something, since actions define beings.
Interfaces are the “brokers” of abilities. A being is said to comply to an interface to become a being of some kind. Here is a duck analogy in interface.
type interfaceIf a being can walk, swim and quack, it’s a Duck. Simple as that. Here are three different breeds of ducks modeled as structs:
type struct type struct type struct
Each breed may take different amount of time to cover the walking or swimming distance given, or some may even quack out loud, but it is definitely a Duck just the same, provided it implements the Duck interface.
// speed is 1 ft per second
return
func
func
// A bluerunner quacks once between walking
return func
func
return
func
The Swim methods are spared for brevity, but you get the idea. Now as all the ducks are “registered”, any function can receive any of them as an argument.
Now, however a duck quack, it can shares one function to execute. This is a powerful pattern of code organization and application modeling.
If a being can walk, swim and quack, it’s a Duck (interface).
Broker or Delegate Pattern
One scenario I’m using is the broker / delegation pattern, which was inspired by Obj-C and Swift programming in iOS. Basically, the concept is a unit of code should be as portable and encapsulated as possible with little or no knowledge of the external code that is using it. If something is considered not within the responsibility of the unit, it should delegate the task to another unit which is better at delegation and coordination.
This is especially useful in avoiding import cycles, which can almost always happen as your code grows. When you import an external package into your project’s package, the cycle import does not happen because the external package is kept intact and never imports your package back.
However, when you deal with imports between your created packages within your project, you can accidentally create an import cycle.
The solution I used was to make sure a package can never be imported directly by another package, and that a code in a package should be as encapsulated as possible. I introduced a “Delegate” struct for each package which implements a broker interface of its own.
This way, a package is always imported via its broker while it import other package’s brokers. Instead of importing a package and using its public function like this:
package importfunc
We can let the packageA’s abroker deals with it by wrapping packageA’s Delegate struct with a broker interface and just import the broker.
var func
This is what abroker might look like:
package importtype
func
return
And for packageA itself, it needs a Delegate struct to instantiate itself as well as make all public functions methods to make the struct implements the ABroker interface:
package type funcThis way all packages are encapsulated and only the brokers are allowed to deal with package imports directly. It can be seen as redundant and repetitive until you know that since go 1.5 there’s a go:generate command that you can use to run a pre-hook code and generate an interface for each package before running the main code.
If you like my write up, be sure to follow my blog Joe Chasinga or twitter @jochasinga or better, offer me a job where I can crack Go all day!