Struct And Interface Embedding
A walkthrough of Embedding in go version 1.14
Embedding¹ is a clever way of extending/composing many types into a single type. It represents the O² part of SOLID³ principles — Open closed principle; open to extension, closed for manipulation. What this means is that we can easily extend a type so that it does the original thing, as well as something else without having to amend the original implementation.
Sample Code for this Post
All running sample code can be found here — https://github.com/ankur22/embedding
Struct Embedding
In the example below, I’ve declared a logger
and a checker
, and each implement a function do
and validate
respectively. I’ve also declared loggerAndChecker
and within it I’ve declared logger
and checker
. loggerAndChecker
now implements both the logger
and checker
functions:
Note how I’ve not named the fields. If I had then loggerAndChecker
would not implement the functions from the other two types implicitly, and I would be required to explicitly call the functions, as in this example:
Also worth noting that loggerAndChecker
can contain its own fields along with the embedded structs.
If either or both the logger
and checker
have a field s
, then the s
declared in loggerAndChecker
would override the s
from the embedded structs. Both the s
values from the embedded structs are still accessible explicitly. If loggerAndChecker
did not declare s
but both logger
and checker
did, we wouldn’t be able to implicitly call s
from loggerAndChecker
, but we can still retrieve each of the s
values from both embedded structs:
Like above, if the logger
and checker
implement the same function (e.g. validate
), and we try and call validate
from loggerAndChecker
it will not compile. However, if we call any other function that do not overlap it will compile and run:
I think it’s a good idea to override the overlapping method, otherwise it will be very annoying for someone who tries to use that function. In the example below I use the validate function from the logger
only:
Interface Embedding
Interface embedding is similar conceptually to struct embedding — we’re embedding interfaces into interfaces instead of structs into structs.
In the following example we have a few interfaces. doer
and validator
each declare functions that should be implemented for a type to qualify to be identified as one or the other. doerAndValidator
doesn’t declare any functions explicitly but instead relies on the declarations of the embedded interfaces.
It’s worth noting that the doesAndValidator
can still declare its own functions too as well as embedding other interfaces:
What if the doer
and the validator
declare the same function? As of go version 1.14, that’s completely fine:
Wait, what about if we two interfaces declare functions with same name, but different signatures? That’s still not ok and it won’t compile since go doesn’t do method overloading⁴ like in other languages such as Java or Python:
Real World Example
In the example below I’ve created a retryableHTTPClient
to perform GET
requests, and if a transient error occurs the request is retried. I’ve built on top of the builtin HTTP client to perform this extra action. retryableHTTPClient
can be passed around anywhere that accepts http.Client
, and the benefits of retry will be reaped there too.
You will see that I’ve used struct embedding of http.Client
into retryableHTTPClient
. We can call all the HTTP client public functions, even if we haven’t implemented them for retryableHTTPClient
as they will fall back to using the HTTP client’s implementation. In this example only the GET
is retryable, so you would need to complete this for the other public methods that also need to be retryable.
Other Articles That Are Also Worth A Read
- https://dave.cheney.net/2020/05/24/diamond-interface-composition-in-go-1-14
- https://golang.org/doc/go1.14
- https://github.com/golang/proposal/blob/master/design/6977-overlapping-interfaces.md
References
[1] https://golang.org/doc/effective_go.html#embedding
[2] https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle
[3] https://en.wikipedia.org/wiki/SOLID
[4] https://www.w3schools.com/java/java_methods_overloading.asp