Design Patterns in Golang: The Good, the Bad and the Ugly

Recently I started a series of articles about Gang of Four Design Patterns and their adoption in Golang. They made a lot of noise in the community. I read a lot of contradictionary opionions whether should be used or not. I am publishing those articles as show case how the common design patterns can be adopted and implemented in Golang. I don’t encourage or promote their usage. Every developer has own style of programming, architecture desing and problem solving solutions.

Well, I don’t have a strong opionion about that. However, I have my own angle of view about this topic. I have never been a strong believer that they should be used intensively in any project. For me they have always been a language for communication among development teams. Yes, they solve particular problems. But I don’t think we should use them, because they exist and ar good practice.

Particular pattern should be used only in concrete case, when we gain benefit of that. I don’t encourage applying it by the book.

The Design Patterns have never been something encourage by the Golang community. They are not idiomatic for the language. Everybody knows that the language itself is very opioninated and idiomatic. There are no so many ways to achieve particular task or solve particular problem.

But let’s explore. Are they used in the existing Golang packages? I will give you a few examples how they are used in Golang libraries:

Singleton Design Pattern

The net/http package has http.DefaultClient and http.DefaultServeMux objects that are alive during the application lifecycle. The DefaultClient is used by Get, Head and Post functions to send request to http server.

Those variables contains a single instances that can be used accros the application. The implementation does not follow the same as most of the mainstream language. It’s still Golang idiomatic.

Factory Design Pattern

Did you used PostrgreSQL library like that?

import (
	"database/sql"
	_ "github.com/lib/pq"
)

func main() {
	db, err := sql.Open("postgres", "user=pqgotest dbname=pqgotest sslmode=verify-full")
	if err != nil {
		log.Fatal(err)
	}

	age := 21
	rows, err := db.Query("SELECT name FROM users WHERE age = $1", age)
	...
}

Well in order to connect to PostgreSQL server the sql package instaciate the registered driver via Factory Design Pattern. The driver is registered by this function.

The Factory function is db.Open.

Iterator Design Pattern

Golang has a token package that defines constants representing the lexical tokens of the Go programming language and basic operations on tokens (printing, predicates).

The package has a token.FileSet struct that represents a set of source files. The struct implements The Interator Design Pattern.

func printStats(d time.Duration) {
 	fileCount := 0
 	lineCount := 0
 	fset.Iterate(func(f *token.File) bool {
 		fileCount++
 		lineCount += f.LineCount()
 		return true
 	})
 	fmt.Printf(
 		"%s (%d files, %d lines, %d lines/s)\n",
 		d, fileCount, lineCount, int64(float64(lineCount)/d.Seconds()),
 	)
 }

It has an Iterate function that calls a function for the files in the file set in the order they were added until it returns false.

Builder or Strategy Design Pattern

The Golang has an image package that can generate and manipulate different formats of images. The package exposes interfaces image.Image and subpackage draw that has draw.Drawer interface.

These interfaces allow composition of different shapes and draw strategies:

// Example from: http://blog.golang.org/go-imagedraw-package
type circle struct {
    p image.Point
    r int
}

func (c *circle) ColorModel() color.Model {
    return color.AlphaModel
}

func (c *circle) Bounds() image.Rectangle {
    return image.Rect(c.p.X-c.r, c.p.Y-c.r, c.p.X+c.r, c.p.Y+c.r)
}

func (c *circle) At(x, y int) color.Color {
    xx, yy, rr := float64(x-c.p.X)+0.5, float64(y-c.p.Y)+0.5, float64(c.r)
    if xx*xx+yy*yy < rr*rr {
        return color.Alpha{255}
    }
    return color.Alpha{0}
}

draw.DrawMask(dst, dst.Bounds(), src, image.ZP, &circle{p, r}, image.ZP, draw.Over)

For me it looks more like Builder Design Pattern or Strategy Design Pattern.

You can read more about it in this Golang blog post.

PS. Please share your finding regarding any other examples of GoF Desing Pattern usage. I will be glad to publish them as well.

Verdict

The Desing Patterns do exist in Golang. Their implementation does not align with the one that we have used to see in the mainstream languages like C#, JAVA and etc.

But what should we consider as idiomatic for Golang?

As my colleague George said:

After all we (as users) define the idioms in the language.

It’s true, isn’t it?

Tags# , ,