I’ve been playing with Go language for a couple of months now, and I’m getting more and more excited about it. All my professional career I have considered myself a Java programmer in the first place, proficient enough in other programming languages and technologies. Entering the rather low-level world of Go was a little shock for my object-orientation sense. The structs and interfaces with runtime polymorphism (i.e. a struct implements an interface when it implements all the method an interface defines, without a need for an explicit declaration) make you think about structuring your code a little differently.
The SOLID principle that is used probably the most widely and gives most benefits when applying is Dependency Inversion Principle. The most in-depth explanation is presented by Uncle Bob Martin, and it goes like this:
A. High level modules should not depend upon low level modules. Both should depend upon abstractions.
B. Abstractions should not depend upon details. Details should depend upon abstractions.
What this means, is that you should not rely on concrete implementations, rather on the interfaces that are defined for these implementations. Take a look at this Java example:
This class would probably would do its job when run, but it introduces a couple of problems. First, it’s pretty hard to write a fast, reliable test for this class. Since it’s always relying on the concrete implementation that makes an external call, you can’t guarantee the results are always the same, and certainly not controlled by you. Also, if you want to provide another implementation instead of REST service, you can’t really do that.
The code above violates the Dependency Inversion Principle: high-level module (
MyService) depends on a low-level module (
RESTService), and the
MyService class depends upon detail (
RESTService). Fortunately, it is usually easy to fix that problem in Java:
Now, the high-level module doesn’t depend on low-level module any more, rather on abstraction (
ExternalService interface), as well as there’s no dependency on details.
I got used to the pattern so much I rarely write code in a different way. Especially when you are using a dependency injection library, like Google Guice, your code tends to be well-structured and easy to maintain.
When I started working on my project in Go, I had some trouble writing clean code. I was trying to get the job fast, so my first attempts looked like something like this:
I immediately ran into problems when trying to write unit tests for that code. It was bound to use Cloud Datastore implementation, so if I couldn’t run the function without access to Google Cloud. I obviously violated Dependency Inversion Principle, and needed to fix that. Fortunately, the awesome types system in Go made it really easy:
I was finally able to work test like I was supposed to do in the beginning. The code above uses function types and passes that as a function parameter. The other way would be making a structs, and pass it as a field. It’s a little more explicit and I feel like a Java crusader trying to apply Java patterns in Go, but this definitely does the job.comments powered by Disqus