When we think of interfaces, as traditional object-oriented programmers, the words ‘abstract’ or ‘implements’ come to mind and the example of the Shape Interface with the Rectangle and Triangle classes materialises before us; or the Animal interface with a Dog and Cat class mulling around. Go thinks differently and hence I take a different (and probably debatable) approach to explain the essence of Interfaces in Go.
The Traditional Approach
Lets design an office with tables and chairs. “But first…” we say, we need to have some abstraction hierarchy around this which will make our design extensible and flexible. Lets say we design the class hierarchy (and I am using Ruby because it’s the easiest for me currently)
class Furniture end class Chair extends Furniture end class Table extends Furniture end
“Hmm.. not good” we say again, it’s not extensible, we should use modules instead because the Chair and Furniture could also be sold — yes, lets create the modules Salable and Furniture!
module Furniture end module Salable end class Chair include Furniture end class Table include Furniture include Salable end
We look back, relax, take a break and say – “Dude – we rock – we have designed an office. Who sells Chairs anyway? ;)”
The Go approach
Let’s design and office with tables and chairs. “Sir, yes Sir!” we say and put on our programmer hat and get down to it!
type Chair struct { Weight float32 Height float32 Color string Material string } func (c Chair) Rotate(degree int) bool { // write some code to rotate the chair // return true if chair has rotated. } func (c Chair) Move(distance int) int { // write some code. // return the distance it moved }
Just like we have a Chair, we can design the Table and its functionality and I am not getting into those details but the ideology behind this.
- We got down to designing the “object” in mind i.e. chairs and tables.
- We did not have to think about any abstraction.
- We are living in the present and not thinking about future use-cases.
Now, suppose, we need to treat chairs and tables as furniture and have put on our Analyst / Architect / Manager hat. We can now implement the Furniture interface
type Furniture interface { Move(distance int) int }
Hey! Chair is Furniture already because it can move. We did not have to “declare” or “implement” it. Suppose we also want to sell furniture.
type Furniture interface { Move(distance int) int Sell(price int) int } func (c Chair) Sell(price int) int { // Beche do! Sell it! }
Hey, Chair is Furniture still – no questions asked. But wait, there’s more -we haven’t quacked yet 😉
“If it quacks, it’s a duck”.
Well, in a galaxy far away, some astronomer defines an interface for the Earth and it’s rotation.
type Rotater interface { Rotate(degree int) bool } type Earth struct { Equator int Continents int } func (e Earth) Rotate(degree int) bool { // the earths rotation! }
Lo and behold, our Chair is also a Rotater along with the Earth because we have implemented the Rotate method for the chair earlier!
If it rotates, it’s a Rotater
I found this pretty intriguing and particularly simple concept as a programmer. We infer the interfaces from the methods and implementation and not from the abstraction and declaration. While traditionally we would have been stuck on debating Salable and Furniture interface, in Go we concentrated on our core object and quickly and easily implemented Chair and extended its functionality. This makes the ideology behind Go interfaces quacking … I mean rocking!
You’re passing this/ref params by value.