Bypassing Golang’s lack of constructors

We can’t interface those with because of Go’s implicit interfaces (there’s no method to add to an interface to say “our object implements this interface now”) and this would be a code smell anyway.

Nil PointersLet’s say we decide that interfacing everything is a stupid idea — it is not pragmatic nor necessary and we would be doing it because of the lack of a language feature, not because we actually need them, which points to this being a code smell.

Instead, let’s publically expose that struct so we can typehint for it and see what can go wrong.

We can pass in a MyStruct initialised only with MyStruct{}.

The function it is passed into calls s.

child, which will be nil, and when we try and call .

num on this nil we will get:panic: runtime error: invalid memory address or nil pointer dereferenceSo we would have to guard against this:Above we declare a function on *MyStruct.

What about if s.

child isn't a pointer but instead is a value (struct)?Empty StructsConsider the following:The != nil check will no longer work.

We will get the following panic:prog.

go:22:16: cannot convert nil to type ChildStructSo instead we have to check for an empty struct:This is just another way through which we have to defend code against nils if we allow the type to be exported.

Where we are nowWe have the following options:Check for a nil for every pointer we have a type declaration for, in every function in the codebaseCheck for an empty struct for every concrete type we have a type declaration for, in every function in the codebaseInterface everything so we can only build objects in a valid state, and then there is no easy way to create an invalid MyStruct – we can't use package1.

myStruct{} and we can't use new(package1.

myStruct) – only package1.

NewMyStruct().

But I don’t want to interface everything!!EncapsulationA language mechanism for restricting direct access to some of the object’s components.

I require the ability to restrict any object’s initialisation to a single entrypoint which guarantees that it cannot be initialised in an invalid state and rightly so, in whatever programming language I am using that has some object oriented capabilites.

But in doing so, in Golang, I require an interface to typehint for it anywhere else because of package visibility and a lack of real constructors.

In PHP for example, we have: __construct() which can be the only way to create an instance of a class, and then via that, and our implementation, we guarantee the object exists in a valid state and we can typehint for that as a dependency elsewhere ensuring we only get that valid object to work on.

It doesn't seem like I can do anything this with Golang.

Is there anything better?Implicit InterfacesGo is special in the way that it’s not like Java, PHP or other OO languages with explicit interfaces.

For example in PHP, our object would implement an interface only when we explicitly type that it does:In Go on the other hand, we have implicit interfaces:Idiomatic Go suggests that sometimes you should define interfaces not next to or near the class that implements them, as you would do with traditional OO languages, but rather alongside the type that will use it.

This is an immensely powerful feature once you get to grips with what it means for your code.

Imagine the following struct again with our New best practice used.

Note that we are not exporting the type, so we can avoid the problems mentioned earlier:Now a struct in Package2 requires the ability to use this struct – but let's think about this for a moment.

What exactly does Package2 want?.- The answer is not the struct, but the ability to call an API method on the struct.

We could go about declaring a globally available interface, which I’ve already elaborated wasn’t the best way of doing things.

We don’t want to interface everything.

But let’s say that our consumer actually only wants to call one method on this struct.

We can define an implicit interface, next to the consumer, only containing the one method!Immediately on creating this interface in Package2, the struct from Package1 can actually fulfil this requirement and fulfil this interface.

Now if we add the following code to Package2, we can take in an instance of MyStruct without explicitly declaring that we need to do so with the struct that is not publically available.

How can we pass in a Package1.

myStruct when it is private?From this we have gained the following:Types are private so they cannot be initialised in an invalid stateWe utilise Go’s equivalent of constructors: factory functions for initialisationIdiomatic Go usage in defining the interface next to the consumerThe implicit interface defined next to the consumer is also Private, which means we’re not going to pollute the global namespace with random interfaces everywhereSmaller interfaces for simpler test mocks — we don’t need every method, only the ones we are implementingAlso adhereing more towards the Interface Segregation Principle of SOLID (smaller, more specific interfaces AKA role interfaces)You can remove all of the nil and empty struct checks that you repeat throughout the application because you have guaranteed that you can no longer have any of your objects in an invalid stateIn the case that you do want to define a public interface to use everywhere, that’s still perfectly fine as well, whether or not it is next to the consumer or traditionally next to the implementationUnderstanding this required a bit of a mindset change for me but it has helped me remove a lot of boilerplate and trust in my code more.

There are always tradeoffs.

Here however, I think there are less chances of error.

ConclusionBut you said you don’t like having to declare an interface for every object!.Well, we’re not creating one huge interface for each struct and that technicality makes the difference.

We are defining specific subsets of functionality that other structs can fulfil next to the consumer!Usually there are tradeoffs to make in any language.

For me, this is just a more fundamental one that I have to consider when designing the code.

I would very much like to be able to request a valid type and not have to check that it has been initialised correctly, as there should only be a specific method of initialisation, and I would like to not have to create one huge interface for every object in the application.

It turns out that Go’s implicit interfaces can provide the safety needed.

I’m really enjoying learning Go and having my mindset challenged and changed as I learn more.

Golang definitely has an interesting way of doing things.

Is this also how you write defensively in Go, or have you found a better way of doing things?.Let me know what you think in the comments.

Originally published at blog.

j7mbo.

com on February 22, 2019.

.. More details

Leave a Reply