DEV Community

Cover image for Golang Patterns - Part 1
Nicola Apicella
Nicola Apicella

Posted on • Edited on

28 3

Golang Patterns - Part 1

Hi everyone!
Today's objective is to describe and document a bunch of golang patterns.

As you know from my previous posts (go concurrency and go lambdas), I have recently entered the Go-land and found out quite soon that common idioms I knew from my previous experience did not quite apply to golang.

Well, I found out that there are some patters that are so common that in my humble opinion should be presented as part of any beginner guide about golang.
All the code in the examples is on github.

Group constants

The title said it all, we want to group common constants in the same namespace.

// Package constants shows a pattern to group constants together
package constants
// Endpoint contains the endpoint configuration
var Endpoint struct {
Hostname string
Port int
}
func init() {
Endpoint.Hostname = "some-endpoint"
Endpoint.Port = 9090
}
view raw constants.go hosted with ❤ by GitHub

Chain

A chain of suppliers that returns as soon as one of the suppliers returns a non-zero result or an error.
The code shows an example in which we need to load a configuration value from one of the possible sources: env variable, config file or a database.
We want to stop searching for the config value as soon as a non-zero result is returned.

package chain
import "fmt"
func ExampleChain() {
endpoint, _ := chain(
loadEndpointFromConfigFile,
loadEndpointFromEnvVariables,
loadEndpointFromDatabase,
).get()
fmt.Println(endpoint)
// Output: some-endpoint
}
func loadEndpointFromEnvVariables() (string, error) {
return "", nil
}
func loadEndpointFromConfigFile() (string, error) {
return "", nil
}
func loadEndpointFromDatabase() (string, error) {
return "some-endpoint", nil
}
view raw chain.go hosted with ❤ by GitHub

Code for the chain here.

Options

Options shows a flexible way to construct an object.
The major benefit is being able to add more parameters in the future to the object constructor without breaking the clients.
In other languages, you would probably use an overloaded constructor or fall back to the builder pattern.

Context("Greeting with no Name option", func() {
It("returns default greeting", func() {
greeting := NewGreeting()
Expect(greeting.get()).To(Equal("Hello Stranger"))
})
})
Context("Greeting with Name option", func() {
It("returns custom greeting", func() {
greeting := NewGreeting(Name("Mickey"))
Expect(greeting.get()).To(Equal("Hello Mickey"))
})
})
view raw options_test.go hosted with ❤ by GitHub

Code for the option here.

Maybe

Maybe is a container which may or may not contain a non-null value.

Context("User present", func() {
var greeting string
MaybeUser(getUser(1)).IfPresent(func(u *User) {
greeting = "Hello " + u.name
})
It("greets the user", func() {
Expect(greeting).To(Equal("Hello Mickey"))
})
})
Context("User absent", func() {
var greeting string
MaybeUser(getUser(-1)).WhenAbsent(func() {
greeting = "Hello stranger"
})
It("greets the user", func() {
Expect(greeting).To(Equal("Hello stranger"))
})
})
view raw maybe_test.go hosted with ❤ by GitHub

Function type

Functions are first class citizen in Golang. They can be used as a type whenever we want to easily implement a Strategy pattern or similar.
This pattern is heavily used in the golang http package.

package functiontype
import "fmt"
type Greeting func(name string) string
func GreetingService(request Request, greeting Greeting) string {
return fmt.Sprintf("Service says: %s", greeting(request.user))
}
func ExampleFunctionType() {
request := Request{user: "Mickey"}
fmt.Println(
GreetingService(request, func(name string) string {
return fmt.Sprintf("Hola %s!", name)
}),
)
// Output: Service says: Hola Mickey!
}
view raw functiontype.go hosted with ❤ by GitHub

Conclusions

Thanks for reading!

Let me know if you found it useful and if there is a key pattern that should really be part of this list.

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (2)

Collapse
 
msoedov profile image
Alex Miasoiedov • Edited

we want to group common constants in the same namespace.

This does not quite fit all use cases, this constants package introduces an inter package/module dependency which not always great especially in service oriented envs

Options shows a flexible way to construct an object.

NewGreeting(Name("Mickey"))
Actually is relatively harder to read and implement comparing to building pattern

NewGreeting().WithName("Mickey")

Collapse
 
napicella profile image
Nicola Apicella

Hi! Thanks for the feedback. I named the package after the example I wanted to show (I.e. maybe package, options package etc.). It is not required to have the constants in a different package (neither is required to name it constants in case you want to have a different package).
As for Options, it s a really valid point and I think the reason why options is more popular than builder(despite the fact that builder is slightly more readable) may be the lack of library like Lombok for example, which generates boiler plate code for you. Options is a bit more light from this point of view, but like you pointed out, might loose a bit in readability.

👋 Kindness is contagious

Engage with a sea of insights in this enlightening article, highly esteemed within the encouraging DEV Community. Programmers of every skill level are invited to participate and enrich our shared knowledge.

A simple "thank you" can uplift someone's spirits. Express your appreciation in the comments section!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found this useful? A brief thank you to the author can mean a lot.

Okay