Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Go Design Patterns
Go Design Patterns

Go Design Patterns: Best practices in software development and CSP

eBook
€8.99 €32.99
Paperback
€41.99
Subscription
Free Trial
Renews at €18.99p/m

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Table of content icon View table of contents Preview book icon Preview Book

Go Design Patterns

Chapter 1. Ready... Steady... Go!

Design Patterns have been the foundation for hundreds of thousands of pieces of software. Since the Gang Of Four (Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides) wrote the book Design Patterns: Elements of Reusable Object-Oriented Software in 1994 with examples in C++ and Smalltalk, the twenty-three classic patterns have been re-implemented in most of major languages of today and they have been used in almost every project you know about.

The Gang of Four detected that many small architectures were present in many of their projects, they started to rewrite them in a more abstract way and they released the famous book.

This book is a comprehensive explanation and implementation of the most common design patterns from the Gang of Four and today's patterns plus some of the most idiomatic concurrency patterns in Go.

But what is Go...?

A little bit of history

On the last 20 years, we have lived an incredible growth in computer science. Storage spaces have been increased dramatically, RAM has suffered a substantial growth, and CPU's are... well... simply faster. Have they grown as much as storage and RAM memory? Not really, CPU industry has reached a limit in the speed that their CPU's can deliver, mainly because they have become so fast that they cannot get enough power to work while they dissipate enough heat. The CPU manufacturers are now shipping more cores on each computer. This situation crashes against the background of many systems programming languages that weren't designed for multi-processor CPUs or large distributed systems that act as a unique machine. In Google, they realized that this was becoming more than an issue while they were struggling to develop distributed applications in languages like Java or C++ that weren't designed with concurrency in mind.

At the same time, our programs were bigger, more complex, more difficult to maintain and with a lot of room for bad practices. While our computers had more cores and were faster, we were not faster when developing our code neither our distributed applications. This was Go's target.

Go design started in 2007 by three Googlers in the research of a programming language that could solve common issues in large scale distributed systems like the ones you can find at Google. The creators were:

  • Rob Pike: Plan 9 and Inferno OS.
  • Robert Griesemer: Worked at Google's V8 JavaScript engine that powers Google Chrome.
  • Ken Thompson: Worked at Bell labs and the Unix team. It has been involved in designing of the Plan 9 operating system as well as the definition of the UTF-8 encoding.

In 2008, the compiler was done and the team got the help of Russ Cox and Ian Lance Taylor. The team started their journey to open source the project in 2009 and in March 2012 they reached a version 1.0 after more than fifty releases.

Installing Go

Any Go Installation needs two basic things: the binaries of the language somewhere on your disk and a GOPATH path in your system where your projects and the projects that you download from other people will be stored.

In the following lines, we will explore how to install Go binaries in Linux, Windows and OS X. For a detailed explanation of how to install the latest version of Go, you can refer to the official documentation at https://golang.org/doc/install.

Linux

To install Go in Linux you have two options:

  • Easy option: Use your distribution package manager:
    • RHEL/Fedora/Centos users with YUM/DNF:sudo yum install -y golang
    • Ubuntu/Debian users using APT with:sudo apt-get install -y golang

  • Advanced: Downloading the latest distribution from https://golang.org.

I recommend using the second and downloading a distribution. Go's updates maintains backward compatibility and you usually should not be worried about updating your Go binaries frequently.

Go Linux advanced installation

The advanced installation of Go in Linux requires you to download the binaries from golang webpage. After entering https://golang.org , click the Download Go button (usually at the right) some Featured Downloads option is available for each distribution. Select Linux distribution to download the latest stable version.

Note

At https://golang.org you can also download beta versions of the language.

Let's say we have saved the tar.gz file in Downloads folder so let's extract it and move it to a different path. By convention, Go binaries are usually placed in /usr/local/go directory:

tar -zxvf go*.*.*.linux-amd64.tar.gz
sudo mv go /usr/local/go

On extraction remember to replace asterisks (*) with the version you have downloaded.

Now we have our Go installation in/usr/local/go path so now we have to add the bin subfolder to our PATH and the bin folder within our GOPATH.

mkdir -p $HOME/go/bin

With -p we are telling bash to create all directories that are necessary. Now we need to append bin folder paths to our PATH, append the following lines at the end of your ~/.bashrc:

export PATH=$PATH:/usr/local/go/bin

Check that our go/bin directory is available:

$ go version
Go version go1.6.2 linux/amd64

Windows

To install Go in Windows, you will need administrator privileges. Open your favorite browser and navigate to https://golang.org . Once there click the Download Go button and select Microsoft Windows distribution. A *.msi file will start downloading.

Execute the MSI installer by double clicking it. An installer will appear asking you to accept the End User License Agreement (EULA) and select a target folder for your installation. We will continue with the default path that in my case was C:\Go.

Once the installation is finished you will have to add the binary Go folder, located in C:\Go\bin to your Path. For this, you must go to Control Panel and select System option. Once in System, select the Advanced tab and click the Environment variables button. Here you'll find a window with variables for your current user and system variables. In system variables, you'll find the Path variable. Click it and click the Edit  button to open a text box. You can add your path by adding ;C:\Go/bin at the end of the current line (note the semicolon at the beginning of the path). In recent Windows versions (Windows 10) you will have a manager to add variables easily.

Mac OS X

In Mac OS X the installation process is very similar to Linux. Open your favorite browser and navigate to https://golang.org and click the Download Go. From the list of possible distributions that appear, select Apple OS X. This will download a *.pkg file to your download folder.

A window will guide you through the installation process where you have to type your administrator password so that it can put Go binary files in /usr/local/go/bin folder with the proper permissions. Now, open Terminal to test the installation by typing this on it:

$ go version
Go version go1.6.2 darwin/amd64

If you see the installed version, everything was fine. If it doesn't work check that you have followed correctly every step or refer to the documentation at https://golang.org.

Setting the workspace - Linux and Apple OS X

Go will always work under the same workspace. This helps the compiler to find packages and libraries that you could be using. This workspace is commonly called GOPATH.

GOPATH has a very important role in your working environment while developing Go software. When you import a library in your code it will search for this library in your $GOPATH/src. The same when you install some Go apps, binaries will be stored in $GOPATH/bin.

At the same, all your source code must be stored in a valid route within $GOPATH/src folder. For example, I store my projects in GitHub and my username is Sayden so, for a project called minimal-mesos-go-framework I will have this folder structure like $GOPATH/src/github.com/sayden/minimal-mesos-go-framework which reflects the URI where this repo is stored at GitHub:

mkdir -p $HOME/go

The $HOME/go path is going to be the destination of our $GOPATH. We have to set an environment variable with our $GOPATH pointing to this folder. To set the environment variable, open again the file $HOME/.bashrc with your favorite text editor and add the following line at the end of it:

export GOPATH=${HOME}/go

Save the file and open a new terminal. To check that everything is working, just write an echo to the $GOPATH variable like this:

echo $GOPATH
/home/mcastro/go

If the output of the preceding command points to your chosen Go path, everything is correct and you can continue to write your first program.

Starting with Hello World

This wouldn't be a good book without a Hello World example. Our Hello World example can't be simpler, open your favorite text editor and create a file called main.go within our $GOPATH/src/[your_name]/hello_world with the following content:

package main 
 
func main(){ 
println("Hello World!") 
} 

Save the file. To run our program, open the Terminal window of your operating system:

  • In Linux, go to programs and find a program called Terminal.
  • In Windows, hit Windows + R, type cmd without quotes on the new window and hit Enter.
  • In Mac OS X, hit Command + Space to open a spotlight search, type terminal without quotes. The terminal app must be highlighted so hit Enter.

Once we are in our terminal, navigate to the folder where we have created our main.go file. This should be under your $GOPATH/src/[your_name]/hello_world and execute it:

go run main.go
Hello World!

That's all. The go run [file] command will compile and execute our application but it won't generate an executable file. If you want just to build it and get an executable file, you must build the app using the following command:

go build -o hello_world

Nothing happens. But if you search in the current directory (ls command in Linux and Mac OS X; and dir in Windows), you'll find an executable file with the name hello_world. We have given this name to the executable file when we wrote -o hello_world command while building. You can now execute this file:

/hello_world
Hello World!

And our message appeared! In Windows, you just need to type the name of the .exe file to get the same result.

Tip

The go run [my_main_file.go] command will build and execute the app without intermediate files. The go build -o [filename] command will create an executable file that I can take anywhere and has no dependencies.

Integrated Development Environment - IDE

An IDE (Integrated Development Environment) is basically a user interface to help developers, code their programs by providing a set of tools to speed up common tasks during development process like compiling, building, or managing dependencies. The IDEs are powerful tools that take some time to master and the purpose of this book is not to explain them (an IDE like Eclipse has its own books).

In Go, you have many options but there are only two that are fully oriented to Go development LiteIDE and Intellij Gogland. LiteIDE is not the most powerful though but Intellij has put lots of efforts to make Gogland a very nice editor with completion, debugging, refactoring, testing, visual coverage, inspections, etc. Common IDEs or text editors that have a Go plugin/integration are as following:

  • IntelliJ Idea
  • Sublime Text 2/3
  • Atom
  • Eclipse

But you can also find Go plugins for:

  • Vim
  • Visual Studio and Visual Code

The IntelliJ Idea and Atom IDEs, for the time of this writing, has the support for debugging using a plugin called Delve. The IntelliJ Idea is bundled with the official Go plugin. In Atom you'll have to download a plugin called Go-plus and a debugger that you can find searching the word Delve.

Types

Types give the user the ability to store values in mnemonic names. All programming languages have types related with numbers (to store integers, negative numbers, or floating point for example) with characters (to store a single character) with strings (to store complete words) and so on. Go language has the common types found in most programming languages:

  • The bool keyword is for Boolean type which represents a True or False state.
  • Many numeric types being the most common:
    • The int type represents a number from 0 to 4294967295 in 32 bits machines and from 0 to 18446744073709551615 in 64 bits.
    • The byte type represents a number from 0 to 255.
    • The float32 and float64 types are the set of all IEEE-754 64/-bit floating-point numbers respectively.
    • You also have signed int type like rune which is an alias of int32 type, a number that goes from -2147483648 to 2147483647 and complex64 and complex128 which are the set of all complex numbers with float32/ float64 real and imaginary parts like 2.0i.

  • The string keyword for string type represents an array of characters enclosed in quotes like "golang" or "computer".
  • An array that is a numbered sequence of elements of a single type and a fixed size (more about arrays later in this chapter). A list of numbers or lists of words with a fixed size is considered arrays.
  • The slice type is a segment of an underlying array (more about this later in this chapter). This type is a bit confusing at the beginning because it seems like an array but we will see that actually, they are more powerful.
  • The structures that are the objects that are composed of another objects or types.
  • The pointers (more about this later in this chapter)are like directions in the memory of our program (yes, like mailboxes that you don't know what's inside).
  • The functions are interesting (more about this later in this chapter). You can also define functions as variables and pass them to other functions (yes, a function that uses a function, did you like Inception movie?).
  • The interface is incredibly important for the language as they provide many encapsulation and abstraction functionalities that we'll need often. We'll use interfaces extensively during the book and they are presented in greater detail later.
  • The map types are unordered key-value structures. So for a given key, you have an associated value.
  • The channels are the communication primitive in Go for concurrency programs. We'll look on channels with more detail on Chapter 8, Dealing with Go's CSP concurrency.

Variables and constants

Variables are spaces in computer's memory to store values that can be modified during the execution of the program. Variables and constants have a type like the ones described in preceding text. Although, you don't need to explicitly write the type of them (although you can do it). This property to avoid explicit type declaration is what is called Inferred types. For example:

    //Explicitly declaring a "string" variable 
    var explicit string = "Hello, I'm a explicitly declared variable" 

Here we are declaring a variable (with the keyword var) called explicit of string type. At the same time, we are defining the value to Hello World!.

    //Implicitly declaring a "string". Type inferred 
inferred := ", I'm an inferred variable " 

But here we are doing exactly the same thing. We have avoided the var keyword and the string type declaration. Internally, Go's compiler will infer (guess) the type of the variable to a string type. This way you have to write much less code for each variable definition.

The following lines use the reflect package to gather information about a variable. We are using it to print the type of (the TypeOf variable in the code) of both variables:

    fmt.Println("Variable 'explicit' is of type:", 
        reflect.TypeOf(explicit)) 
    fmt.Println("Variable 'inferred' is of type:", 
        reflect.TypeOf(inferred)) 

When we run the program, the result is the following:

$ go run main.go
Hello, I'm a explicitly declared variable
Hello, I'm an inferred variable
Variable 'explicit' is of type: string
Variable 'inferred' is of type: string

As we expected, the compiler has inferred the type of the implicit variable to string too. Both have written the expected output to the console.

Operators

The operators are used to perform arithmetic operations and make comparisons between many things. The following operators are reserved by Go language.

Operators

Most commonly used operators are the arithmetic operators and comparators. Arithmetic operators are as following:

  • The + operator for sums
  • The - operator for subtractions
  • The * operator for multiplications
  • The / operator for divisions
  • The % operator for division remainders
  • The ++ operator to add 1 to the current variable
  • The -- operator to subtract 1 to the current variable

On the other side, comparators are used to check the differences between two statements:

  • The == operator to check if two values are equal
  • The != operator to check if two values are different
  • The > operator to check if left value is higher than right value
  • The < operator to check if left value is lower than right value
  • The >= operator to check if left value is higher or equal to right value
  • The <= operator to check if left value is lower or equal to right value
  • The &&operator to check if two values are true

You also have the shifters to perform a binary shift to left or right of a value and a negated operator to invert some value. We´ll use these operators a lot during the following chapters so don´t worry too much about them now, just keep in mind that you cannot set the name of any variable, field or function in your code like this operators.

Tip

What's the inverted value of 10? What's the negated value of 10? -10? Incorrect.. 10 in binary code is 1010 so if we negate every number we will have 0101 or 101 which is the number 5.

Flow control

Flow control is referred as the ability to decide which portion of code or how many times you execute some code on a condition. In Go, it is implemented using familiar imperative clauses like if, else, switch and for. The syntax is easy to grasp. Let´s review major flow control statements in Go.

The if... else statement

Go language, like most programming languages, has if…else conditional statement for flow control. The Syntax is similar to other languages but you don't need to encapsulate the condition between parenthesis:

ten := 10 
if ten == 20 { 
    println("This shouldn't be printed as 10 isn't equal to 20") 
} else { 
    println("Ten is not equals to 20"); 
} 

The else...if condition works in a similar fashion, you don't need parentheses either and they are declared as programmer would expect:

if "a" == "b" ||  10 == 10 || true == false { 
    println("10 is equal to 10") 
  } else if 11 == 11 &&"go" == "go" { 
  println("This isn't print because previous condition was satisfied"); 
    } else { 
        println("In case no condition is satisfied, print this") 
    } 
} 

Note

Go does not have ternary conditions like condition ? true : false.

The switch statement

The switch statement is also similar to most imperative languages. You take a variable and check possible values for it:

number := 3 
switch(number){ 
    case 1: 
        println("Number is 1") 
    case 2: 
        println("Number is 2") 
    case 3: 
        println("Number is 3") 
} 

The for…range statement

The _for_ loop is also similar than in common programming languages but you don't use parentheses either

for i := 0; i<=10; i++ { 
    println(i) 
} 

As you have probably imagined if you have computer science background, we infer an int variable defined as 0 and execute the code between the brackets while the condition (i<=10) is satisfied. Finally, for each execution, we added 1 to the value of i. This code will print the numbers from 0 to 10. You also have a special syntax to iterate over arrays or slices which is range:

for index, value := range my_array { 
    fmt.Printf("Index is %d and value is %d", index, value) 
} 

First, the fmt (format) is a very common Go package that we will use extensively to give shape to the message that we will print in the console.

Regarding for, you can use the range keyword to retrieve every item in a collection like my_array and assign them to the value temporal variable. It will also give you an index variable to know the position of the value you're retrieving. It's equivalent to write the following:

for index := 0, index < len(my_array); index++ { 
    value := my_array[index] 
    fmt.Printf("Index is %d and value is %d", index, value) 
} 

Tip

The len method is used to know the length of a collection.

If you execute this code, you'll see that the result is the same.

Functions

A function is a small portion of code that surrounds some action you want to perform and returns one or more values (or nothing). They are the main tool for developer to maintain structure, encapsulation, and code readability but also allow an experienced programmer to develop proper unit tests against his or her functions.

Functions can be very simple or incredibly complex. Usually, you'll find that simpler functions are also easier to maintain, test and debug. There is also a very good advice in computer science world that says: A function must do just one thing, but it must do it damn well.

What does a function look like?

A function is a piece of code with its own variables and flow that doesn't affect anything outside of the opening and close brackets but global package or program variables. Functions in Go has the following composition:

func [function_name] (param1 type, param2 type...) (returned type1, returned type2...) { 
    //Function body 
} 

Following the previous definition, we could have the following example:

func hello(message string) error { 
    fmt.Printf("Hello %s\n", message) 
    return nil 
} 

Functions can call other functions. For example, in our previous hello function, we are receiving a message argument of type string and we are calling a different function fmt.Printf("Hello %s\n", message) with our argument as parameter. Functions can also be used as parameters when calling other functions or be returned.

It is very important to choose a good name for your function so that it is very clear what it is about without writing too many comments over it. This can look a bit trivial but choosing a good name is not so easy. A short name must show what the function does and let the reader imagine what error is it handling or if it's doing any kind of logging. Within your function, you want to do everything that a particular behavior need but also to control expected errors and wrapping them properly.

So, to write a function is more than simply throw a couple of lines that does what you need, that's why it is important to write a unit test, make them small and concise.

What is an anonymous function?

An anonymous function is a function without a name. This is useful when you want to return a function from another function that doesn't need a context or when you want to pass a function to a different function. For example, we will create a function that accepts one number and returns a function that accepts a second number that it adds it to the first one. The second function does not have a declarative name (as we have assigned it to a variable) that is why it is said to be anonymous:

func main(){ 
    add := func(m int){ 
         return m+1 
} 
 
    result := add(6) 
 
    //1 + 6 must print 7 
    println(result) 
} 

The add variable points to an anonymous function that adds one to the specified parameter. As you can see, it can be used only for the scope its parent function main and cannot be called from anywhere else.

Anonymous functions are really powerful tools that we will use extensively on design patterns.

Closures

Closures are something very similar to anonymous functions but even more powerful. The key difference between them is that an anonymous function has no context within itself and a closure has. Let's rewrite the previous example to add an arbitrary number instead of one:

func main(){ 
    addN := func(m int){ 
        return func(n int){ 
            return m+n 
        }            
    } 
 
    addFive := addN(5) 
    result := addN(6)  
    //5 + 6 must print 7 
    
    println(result) 
}

The addN variable points to a function that returns another function. But the returned function has the context of the m parameter within it. Every call to addN will create a new function with a fixed m value, so we can have main addN functions, each adding a different value.

This ability of closures is very useful to create libraries or deal with functions with unsupported types.

Creating errors, handling errors and returning errors.

Errors are extensively used in Go, probably thanks to its simplicity. To create an error simply make a call to errors.New(string) with the text you want to create on the error. For example:

err := errors.New("Error example") 

As we have seen before, we can return errors to a function. To handle an error you'll see the following pattern extensively in Go code:

func main(){ 
    err := doesReturnError() 
    if err != nil { 
        panic(err) 
    } 
} 
 
func doesReturnError() error { 
    err := errors.New("this function simply returns an error") 
    return err 
} 

Function with undetermined number of parameters

Functions can be declared as variadic. This means that its number of arguments can vary. What this does is to provide an array to the scope of the function that contains the arguments that the function was called with. This is convenient if you don't want to force the user to provide an array when using this function. For example:

func main() { 
    fmt.Printf("%d\n", sum(1,2,3)) 
    fmt.Printf("%d\n", sum(4,5,6,7,8)) 
} 
 
func sum(args ...int) (result int) { 
    for _, v := range args { 
        result += v 
    } 
    return 
} 

In this example, we have a sum function that will return the sum of all its arguments but take a closer look at the main function where we call sum. As you can see now, first we call sum with three arguments and then with five arguments. For sum functions, it doesn't matter how many arguments you pass as it treats its arguments as an array all in all. So on our sum definition, we simply iterate over the array to add each number to the result integer.

Naming returned types

Have you realized that we have given a name to the returned type? Usually, our declaration would be written as func sum(args int) int but you can also name the variable that you'll use within the function as a return value. Naming the variable in the return type would also zero-value it (in this case, an int will be initialized as zero). At the end, you just need to return the function (without value) and it will take the respective variable from the scope as returned value. This also makes easier to follow the mutation that the returning variable is suffering as well as to ensure that you aren't returning a mutated argument.

Arrays, slices, and maps

Arrays are one of the most widely used types of computer programming. They are lists of other types that you can access by using their position on the list. The only downside of an array is that its size cannot be modified. Slices allow the use of arrays with variable size. The maps type will let us have a dictionary like structures in Go. Let's see how each work.

Arrays

An array is a numbered sequence of elements of a single type. You can store 100 different unsigned integers in a unique variable, three strings or 400 bool values. Their size cannot be changed.

You must declare the length of the array on its creation as well as the type. You can also assign some value on creation. For example here you have 100 int values all with 0 as value:

var arr [100]int 

Or an array of size 3 with strings already assigned:

arr := [3]string{"go", "is", "awesome"} 

Here you have an array of 2 bool values that we initialize later:

var arr [2]bool 
arr[0] = true 
arr[1] = false 

Zero-initialization

In our previous example, we have initialized an array of bool values of size 2. We wouldn't need to assign arr[1] to false because of the nature of zero-initialization in the language. Go will initialize every value in a bool array to false. We will look deeper to zero-initialization later in this chapter.

Slices

Slices are similar to arrays, but their size can be altered on runtime. This is achieved, thanks to the underlying structure of a slice that is an array. So, like arrays, you have to specify the type of the slice and its size. So, use the following line to create a slice:

mySlice := make([]int, 10) 

This command has created an underlying array of ten elements. If we need to change the size of the slice by, for example, adding a new number, we would append the number to the slice:

mySlice := append(mySlice, 5) 

The syntax of append is of the form ([array to append an item to], [item to append]) and returns the new slice, it does not modify the actual slice. This is also true to delete an item. For example, let's delete the first item of the array as following:

mySlice := mySlice[1:] 

Yes, like in arrays. But what about deleting the second item? We use the same syntax:

mySlice = append(mySlice[:1], mySlice[2:]...) 

We take all elements from zero index (included) to the first index (not included) and each element from the second index (included) to the end of the array, effectively deleting the value at the second position in the slice (index 1 as we start counting with 0). As you can see, we use the undetermined arguments syntax as the second parameter.

Maps

Maps are like dictionaries--for each word, we have a definition but we can use any type as word or definition and they'll never be ordered alphabetically. We can create maps of string that point to numbers, a string that points to interfaces and structs that point to int and int to function. You cannot use as key: slices, the functions, and maps. Finally, you create maps by using the keyword make and specifying the key type and the value type:

myMap := make(map[string]int) 
myMap["one"] = 1 
myMap["two"] = 2 
fmt.Println(myMap["one"]) 

When parsing JSON content, you can also use them to get a string[interface] map:

myJsonMap := make(map[string]interface{}) 
jsonData := []byte(`{"hello":"world"}`) 
err := json.Unmarshal(jsonData, &myJsonMap) 
if err != nil { 
panic(err) 
} 
fmt.Printf("%s\n", myJsonMap["hello"]) 

The myJsonMap variable is a map that will store the contents of JSON and that we will need to pass its pointer to the Unmarshal function. The jsonData variable declares an array of bytes with the typical content of a JSON object; we are using this as the mock object. Then, we unmarshal the contents of the JSON storing the result of the memory location of myJsonMap variable. After checking that the conversion was ok and the JSON byte array didn't have syntax mistakes, we can access the contents of the map in a JSON-like syntax.

Visibility

Visibility is the attribute of a function or a variable to be visible to different parts of the program. So a variable can be used only in the function that is declared, in the entire package or in the entire program.

How can I set the visibility of a variable or function? Well, it can be confusing at the beginning but it cannot be simpler:

  • Uppercase definitions are public (visible in the entire program).
  • Lowercase are private (not seen at the package level) and function definitions (variables within functions) are visible just in the scope of the function.

Here you can see an example of a public function:

package hello 
 
func Hello_world(){ 
    println("Hello World!") 
} 

Here, Hello_world is a global function (a function that is visible across the entire source code and to third party users of your code). So, if our package is called hello, we could call this function from outside of this package by using hello.Hello_world() method.

package different_package 
 
import "github.com/sayden/go-design-patters/first_chapter/hello" 
 
func myLibraryFunc() { 
hello.Hello_world() 
} 

As you can see, we are in the different_package package. We have to import the package we want to use with the keyword import. The route then is the path within your $GOPATH/src that contains the package we are looking for. This path conveniently matches the URL of a GitHub account or any other Concurrent Versions System(CVS) repository.

Zero-initialization

Zero-initialization is a source of confusion sometimes. They are default values for many types that are assigned even if you don't provide a value for the definition. Following are the zero-initialization for various types:

  • The false initialization for bool type.
  • Using 0 values for int type.
  • Using 0.0 for float type.
  • Using "" (empty strings) for string type.
  • Using nil keyword for pointers, functions, interfaces, slices, channels and maps.
  • Empty struct for structures without fields.
  • Zero-initialized struct for structures with fields. The zero value of a structure is defined as the structure that has its fields initialized as zero value too.

Zero-initialization is important when programming in Go because you won't be able to return a nil value if you have to return an int type or a struct. Keep this in mind, for example, in functions where you have to return a bool value. Imagine that you want to know if a number is divisible by a different number but you pass 0 (zero) as the divisor.

func main() { 
    res := divisibleBy(10,0) 
    fmt.Printf("%v\n", res) 
} 
 
func divisibleBy(n, divisor int) bool { 
    if divisor == 0 { 
        //You cannot divide by zero 
        return false 
    } 
 
    return (n % divisor == 0) 
} 

The output of this program is false but this is incorrect. A number divided by zero is an error, it's not that 10 isn't divisible by zero but that a number cannot be divided by zero by definition. Zero-initialization is making things awkward in this situation. So, how can we solve this error? Consider the following code:

func main() { 
    res, err := divisibleBy(10,0) 
    if err != nil { 
log.Fatal(err) 
    } 
 
    log.Printf("%v\n", res) 
} 
 
func divisibleBy(n, divisor int) (bool, error) { 
    if divisor == 0 { 
        //You cannot divide by zero 
        return false, errors.New("A number cannot be divided by zero") 
    } 
 
    return (n % divisor == 0), nil 
} 

We're dividing 10 by 0 again but now the output of this function is A number cannot be divided by zero. Error captured, the program finished gracefully.

Pointers and structures

Pointers are the number one source of a headache of every C or C++ programmer. But they are one of the main tools to achieve high-performance code in non-garbage-collected languages. Fortunately for us, Go's pointers have achieved the best of both worlds by providing high-performance pointers with garbage-collector capabilities and easiness.

On the other side for its detractors, Go lacks inheritance in favor of composition. Instead of talking about the objects that are in Go, your objects have other . So, instead of having a car structure that inherits the class vehicle (a car is a vehicle), you could have a vehicle structure that contains a car structure within.

What is a pointer? Why are they good?

Pointers are hated, loved, and very useful at the same time. To understand what a pointer is can be difficult so let's try with a real world explanation. As we mentioned earlier in this chapter, a pointer is a like a mailbox. Imagine a bunch of mailboxes in a building; all of them have the same size and shape but each refers to a different house within the building. Just because all mailboxes are the same size does not mean that each house will have the same size. We could even have a couple of houses joined, a house that was there but now has a license of commerce, or a house that is completely empty. So the pointers are the mailboxes, all of them of the same size and that refer to a house. The building is our memory and the houses are the types our pointers refer to and the memory they allocate. If you want to receive something in your house, it's far easier to simply send the address of your house (to send the pointer) instead of sending the entire house so that your package is deposited inside. But they have some drawbacks as if you send your address and your house (variable it refers to) disappears after sending, or its type owner change--you'll be in trouble.

How is this useful? Imagine that somehow you have 4 GB of data in a variable and you need to pass it to a different function. Without a pointer, the entire variable is cloned to the scope of the function that is going to use it. So, you'll have 8 GB of memory occupied by using this variable twice that, hopefully, the second function isn't going to use in a different function again to raise this number even more.

You could use a pointer to pass a very small reference to this chunk to the first function so that just the small reference is cloned and you can keep your memory usage low.

While this isn't the most academic nor exact explanation, it gives a good view of what a pointer is without explaining what a stack or a heap is or how they work in x86 architectures.

Pointers in Go are very limited compared with C or C++ pointers. You can't use pointer arithmetic nor can you create a pointer to reference an exact position in the stack.

Pointers in Go can be declared like this:

number := 5 

Here number := 5 code represents our 4 GB variable and pointer_to_number contains the reference (represented by an ampersand) to this variable. It's the direction to the variable (the one that you put in the mailbox of this house/type/variable). Let's print the variable pointer_to_number , which is a simple variable:

println(pointer_to_number) 
0x005651FA 

What's that number? Well, the direction to our variable in memory. And how can I print the actual value of the house? Well, with an asterisk (*) we tell the compiler to take the value that the pointer is referencing, which is our 4 GB variable.

 println(*pointer_to_number) 
5 

Structs

A struct is an object in Go. It has some similarities with classes in OOP as they have fields. Structs can implement interfaces and declare methods. But, for example, in Go, there's not inheritance. Lack of inheritance looks limiting but in fact, composition over inheritance was a requirement of the language.

To declare a structure, you have to prefix its name with the keyword type and suffix with the keyword struct and then you declare any field or method between brackets, for example:

type Person struct { 
    Name string 
    Surname string 
    Hobbies []string 
    id string 
} 

In this piece of code, we have declared a Person structure with three public fields (Name, Age , and Hobbies) and one private field (id, if you recall the Visibility section in this chapter, lowercase fields in Go refers to private fields are just visible within the same package). With this struct, we can now create as many instances of Person as we want. Now we will write a function called GetFullName that will give the composition of the name and the surname of the struct it belongs to:

func (person *Person) GetFullName() string { 
    return fmt.Sprintf("%s %s", person.Name, person.Surname) 
} 
 
func main() { 
    p := Person{ 
        Name: "Mario", 
        Surname: "Castro", 
        Hobbies: []string{"cycling", "electronics", "planes"}, 
        id: "sa3-223-asd", 
    } 
 
    fmt.Printf("%s likes %s, %s and %s\n", p.GetFullName(), p.Hobbies[0], p.Hobbies[1], p.Hobbies[2]) 
} 

Methods are defined similarly to functions but in a slightly different way. There is a(p *Person) that refers to a pointer to the created instance of the struct (recall the Pointers section in this chapter). It's like using the keyword this in Java or self in Python when referring to the pointing object.

Maybe you are thinking why does (p *Person) have the pointer operator to reflect that p is actually a pointer and not a value? This is because you can also pass Person by value by removing the pointer signature, in which case a copy of the value of Person is passed to the function. This has some implications, for example, any change that you make in p if you pass it by value won't be reflected in source p. But what about our GetFullName() method?

func (person Person) GetFullName() string { 
    return fmt.Sprintf("%s %s", person.Name, person.Surname) 
} 

Its console output has no effect in appearance but a full copy was passed before evaluating the function. But if we modify person here, the source p won't be affected and the new person value will be available only on the scope of this function.

On the main function, we create an instance of our structure called p. As you can see, we have used implicit notation to create the variable (the := symbol). To set the fields, you have to refer to the name of the field, colon, the value, and the comma (don't forget the comma at the end!). To access the fields of the instantiated structure, we just refer to them by their name like p.Name or p.Surname. You use the same syntax to access the methods of the structure like p.GetFullName().

The output of this program is:

$ go run main.go 
Mario Castro likes cycling, electronics and planes

Structures can also contain another structure (composition) and implement interface methods apart from their own but, what's an interface method?

Interfaces

Interfaces are essential in object-oriented programming, in functional programming (traits) and, especially, in design patterns. Go's source code is full of interfaces everywhere because they provide the abstraction needed to deliver uncoupled code with the help of functions. As a programmer, you also need this type of abstraction when you write libraries but also when you write code that is going to be maintained in the future with new functionality.

Interfaces are something difficult to grasp at the beginning but very easy once you have understood their behavior and provide very elegant solutions for common problems. We will use them extensively during this book so put special focus on this section.

Interfaces - signing a contract

An interface is something really simple but powerful. It's usually defined as a contract between the objects that implement it but this explanation isn't clear enough in my honest opinion for newcomers to the interface world.

A water-pipe is a contract too; whatever you pass through it must be a liquid. Anyone can use the pipe, and the pipe will transport whatever liquid you put in it (without knowing the content). The water-pipe is the interface that enforces that the users must pass liquids (and not something else).

Let's think about another example: a train. The railroads of a train are like an interface. A train must construct (implement) its width with a specified value so that it can enter the railroad but the railroad never knows exactly what it's carrying (passengers or cargo). So for example, an interface of the railroad will have the following aspect:

type RailroadWideChecker interface { 
    CheckRailsWidth() int 
} 

The RailroadWideChecker is the type our trains must implement to provide information about their width. The trains will verify that the train isn't too wide or too narrow to use its railroads:

type Railroad struct { 
    Width int 
} 
 
func (r *Railroad) IsCorrectSizeTrain(r RailRoadWideChecker) bool { 
    return r.CheckRailsWidth() != r.Width 
} 

The Railroad is implemented by an imaginary station object that contains the information about the width of the railroads in this station and that has a method to check whether a train fits the needs of the railroad with the IsCorrectSizeTrain method. The IsCorrectSizeTrain method receives an interface object which is a pointer to a train that implements this interface and returns a validation between the width of the train and the railroad:

Type Train struct { 
    TrainWidth int 
} 
 
func (p *Train) CheckRailsWidth() int { 
    return p.TrainWidth 
} 

Now we have created a passenger's train. It has a field to contain its width and implements our CheckRailsWidth interface method. This structure is considered to fulfill the needs of a RailRoadWideChecker interface (because it has an implementation of the methods that the interfaces ask for).

So now, we'll create a railroad of 10 units wide and two trains--one of 10 units wide that fit the railroad size and another of 15 units that cannot use the railroad.

func main(){ 
    railroad := Railroad{Width:10} 
 
    passengerTrain := Train{TrainWidth: 10} 
    cargoTrain := Train {TrainWidth: 15} 
 
    canPassengerTrainPass := railroad.IsCorrectSizeTrain(passengerTrain) 
    canCargoTrainPass := railroad.IsCorrectSizeTrain(cargoTrain) 
 
    fmt.Printf("Can passenger train pass? %b\n", canPassengerTrainPass) 
    fmt.Printf("Can cargo train pass? %b\n", canCargoTrainPass) 
} 

Let's dissect this main function. First, we created a railroad object of 10 units called railroad. Then two trains, of 10 and 15 units' width for passengers and cargo respectively. Then, we pass both objects to the railroad method that accepts interfaces of the RailroadWideChecker interface. The railroad itself does not know the width of each train separately (we'll have a huge list of trains) but it has an interface that trains must implement so that it can ask for each width and returns a value telling you if a train can or cannot use of the railroads. Finally, the output of the call to printf function is the following:

Can passenger train pass? true
Can cargo train pass? false

As I mentioned earlier, interfaces are so widely used during this book that it doesn't matter if it still looks confusing for the reader as they'll be plenty of examples during the book.

Testing and TDD

When you write the first lines of some library, it's difficult to introduce many bugs. But once the source code gets bigger and bigger, it becomes easier to break things. The team grows and now many people are writing the same source code, new functionality is added on top of the code that you wrote at the beginning. And code stopped working by some modification in some function that now nobody can track down.

This is a common scenario in enterprises that testing tries to reduce (it doesn't completely solve it, it's not a holy grail). When you write unit tests during your development process, you can check whether some new feature is breaking something older or whether your current new feature is achieving everything expected in the requirements.

Go has a powerful testing package that allows you also to work in a TDD environment quite easily. It is also very convenient to check the portions of your code without the need to write an entire main application that uses it.

The testing package

Testing is very important in every programming language. Go creators knew it and decided to provide all libraries and packages needed for the test in the core package. You don't need any third-party library for testing or code coverage.

The package that allows for testing Go apps is called, conveniently, testing. We will create a small app that sums two numbers that we provide through the command line:

func main() { 
    //Atoi converts a string to an int 
    a, _ := strconv.Atoi(os.Args[1]) 
    b, _ := strconv.Atoi(os.Args[2]) 
 
    result := sum(a,b) 
    fmt.Printf("The sum of %d and %d is %d\n", a, b, result) 
} 
 
func sum(a, b int) int { 
    return a + b 
} 

Let's execute our program in the terminal to get the sum:

$ go run main.go 3 4
The sum of 3 and 4 is 7

By the way, we're using the strconv package to convert strings to other types, in this case, to int. The method Atoi receives a string and returns an int and an error that, for simplicity, we are ignoring here (by using the underscore).

Tip

You can ignore variable returns by using the underscores if necessary, but usually, you don't want to ignore errors.

Ok, so let's write a test that checks the correct result of the sum. We're creating a new file called main_test.go. By convention, test files are named like the files they're testing plus the _test suffix:

func TestSum(t *testing.T) { 
    a := 5 
    b := 6 
    expected := 11 
 
    res := sum(a, b) 
    if res != expected { 
        t.Errorf("Our sum function doens't work, %d+%d isn't %d\n", a, b, res) 
    } 
} 

Testing in Go is used by writing methods started with the prefix Test, a test name, and the injection of the testing.T pointer called t. Contrary to other languages, there are no asserts nor special syntax for testing in Go. You can use Go syntax to check for errors and you call t with information about the error in case it fails. If the code reaches the end of the Test function without arising errors, the function has passed the tests.

To run a test in Go, you must use the go test -v command (-v is to receive verbose output from the test) keyword, as following:

$ go test -v
=== RUN   TestSum
--- PASS: TestSum (0.00s)
PASS
ok   github.com/go-design-patterns/introduction/ex_xx_testing 0.001s

Our tests were correct. Let's see what happens if we break things on purpose and we change the expected value of the test from 11 to 10:

$ go test
--- FAIL: TestSum (0.00s)
    main_test.go:12: Our sum function doens't work, 5+6 isn't 10
FAIL
exit status 1
FAIL  github.com/sayden/go-design-patterns/introduction/ex_xx_testing 0.002s

The test has failed (as we expected). The testing package provides the information you set on the test. Let's make it work again and check test coverage. Change the value of the variable expected from 10 to 11 again and run the command go test -cover to see code coverage:

$ go test -cover
PASS
coverage: 20.0% of statements
ok  github.com/sayden/go-design-patterns/introduction/ex_xx_testing 0.001s

The -cover options give us information about the code coverage for a given package. Unfortunately, it doesn't provide information about overall application coverage.

What is TDD?

TDD is the acronym for Test Driven Development. It consists of writing the tests first before writing the function (instead of what we did just before when we wrote the sum function first and then we wrote the test function).

TDD changes the way to write code and structure code so that it can be tested (a lot of code you can find in GitHub, even code that you have probably written in the past is probably very difficult, if not impossible, to test).

So, how does it work? Let's explain this with a real life example--imagine that you are in summer and you want to be refreshed somehow. You can build a pool, fill it with cold water, and jump into it. But in TDD terms, the steps will be:

  1. You jump into a place where the pool will be built (you write a test that you know it will fail).
  2. It hurts... and you aren't cool either (yes... the test failed, as we predicted).
  3. You build a pool and fill it with cold water (you code the functionality).
  4. You jump into the pool (you repeat the point 1 test again).
  5. You're cold now. Awesome! Object completed (test passed).
  6. Go to the fridge and take a beer to the pool. Drink. Double awesomeness (refactor the code).

So let's repeat the previous example but with a multiplication. First, we will write the declaration of the function that we're going to test:

func multiply(a, b int) int { 
    return 0 
} 

Now let's write the test that will check the correctness of the previous function:

import "testing" 
 
func TestMultiply(t *testing.T) { 
    a := 5 
    b := 6 
    expected := 30 
 
    res := multiply(a, b) 
    if res != expected { 
        t.Errorf("Our multiply function doens't work, %d*%d isn't %d\n", a, b, res) 
    } 
} 

And we test it through the command line:

$ go test
--- FAIL: TestMultiply (0.00s)
main_test.go:12: Our multiply function doens't work, 5+6 isn't 0
FAIL
exit status 1
FAIL    github.com/sayden/go-designpatterns/introduction/ex_xx_testing/multiply    

0.002s

Nice. Like in our pool example where the water wasn't there yet, our function returns an incorrect value too. So now we have a function declaration (but isn't defined yet) and the test that fails. Now we have to make the test pass by writing the function and executing the test to check:

func multiply(a, b int) int { 
    return a*b 
} 

And we execute again our testing suite. After writing our code correctly, the test should pass so we can continue to the refractoring process:

$ go test
PASS
ok      github.com/sayden/go-design-patterns/introduction/ex_xx_testing/multiply    
0.001s

Great! We have developed the multiply function following TDD. Now we must refactor our code but we cannot make it more simple or readable so the loop can be considered closed.

During this book, we will write many tests that define the functionality that we want to achieve in our patterns. TDD promotes encapsulation and abstraction (just like design patterns do).

Libraries

Until now, most of our examples were applications. An application is defined by its main function and package. But with Go, you can also create pure libraries. In libraries, the package need not be called main nor do you need the main function.

As libraries aren't applications, you cannot build a binary file with them and you need the main package that is going to use them.

For example, let's create an arithmetic library to perform common operations on integers: sums, subtractions, multiplications, and divisions. We'll not get into many details about the implementation to focus on the particularities of Go's libraries:

package arithmetic 
 
func Sum(args ...int) (res int) { 
    for _, v := range args { 
        res += v 
    } 
    return 
} 

First, we need a name for our library; we set this name by giving a name to the entire package. This means that every file in this folder must have this package name too and the entire group of files composes the library called arithmetic too in this case (because it only contains one package). This way, we won't need to refer to the filenames for this library and to provide the library name and path will be enough to import and use it. We have defined a Sum function that takes as many arguments as you need and that will return an integer that, during the scope of the function, is going to be called res. This allows us to initialize to 0 the value we're returning. We defined a package (not the main package but a library one) and called it arithmetic. As this is a library package, we can't run it from the command line directly so we'll have to create the main function for it or a unit test file. For simplicity , we'll create a main function that runs some of the operations now but let's finish the library first:

func Subtract(args ...int) int { 
    if len(args) < 2 { 
        return 0 
    } 
 
    res := args[0] 
    for i := 1; i < len(args); i++ { 
        res -= args[i] 
    } 
    return res 
} 

The Subtraction code will return 0 if the number of arguments is less than zero and the subtraction of all its arguments if it has two arguments or more:

func Multiply(args ...int) int { 
    if len(args) < 2 { 
        return 0 
    } 
 
    res := 1 
    for i := 0; i < len(args); i++ { 
        res *= args[i] 
    } 
    return res 
} 

The Multiply function works in a similar fashion. It returns 0 when arguments are less than two and the multiplication of all its arguments when it has two or more. Finally, the Division code changes a bit because it will return an error if you ask it to divided by zero:

func Divide(a, b int) (float64, error) { 
    if b == 0 { 
        return 0, errors.New("You cannot divide by zero") 
    }  
    return float64(a) / float64(b), nil 
} 

So now we have our library finished, but we need a main function to use it as libraries cannot be converted to executable files directly. Our main function looks like the following:

package main 
 
import ( 
"fmt" 
 
"bitbucket.org/mariocastro/go-design-patterns/introduction/libraries/arithmetic" 
) 
 
func main() { 
    sumRes := arithmetic.Sum(5, 6) 
    subRes := arithmetic.Subtract(10, 5) 
    multiplyRes := arithmetic.Multiply(8, 7) 
    divideRes, _ := arithmetic.Divide(10, 2) 
 
    fmt.Printf("5+6 is %d. 10-5 is %d, 8*7 is %d and 10/2 is %f\n", sumRes, subRes, multiplyRes, divideRes) 
} 

We are performing an operation over every function that we have defined. Take a closer look at the import clause. It is taking the library we have written from its folder within $GOPATH that matches its URL in https://bitbucket.org/ . Then, to use every one of the functions that are defined within a library, you have to name the package name that the library has before each method.

Note

Have you realized that we called our functions with uppercase names? Because of the visibility rules we have seen before, exported functions in a package must have uppercase names or they won't be visible outside of the scope of the package. So, with this rule in mind, you cannot call a lowercase function or variable within a package and package calls will always be followed by uppercase names.

Let's recall, some naming conventions about libraries:

  • Each file in the same folder must contain the same package name. Files don't need to be named in any special way.
  • A folder represents a package name within a library. The folder name will be used on import paths and it doesn't need to reflect the package name (although it's recommended for the parent package).
  • A library is one or many packages representing a tree that you import by the parent of all packages folder.
  • You call things within a library by their package name.

The Go get tool

Go get is a tool to get third party projects from CVS repositories. Instead of using the git clone command, you can use Go get to receive a series of added benefits. Let's write an example using CoreOS's ETCD project which is a famous distributed key-value store.

CoreOS's ETCD is hosted on GitHub at  https://github.com/coreos/etcd.git. To download this project source code using the Go get tool, we must type in the Terminal it's resulting import path that it will have in our GOPATH:

$ go get github.com/coreos/etcd

Note that we have just typed the most relevant information so that Go get figures out the rest. You'll get some output, depending on the state of the project, but after, while, it will disappear. But what did happen?

  • Go get has created a folder in $GOPATH/src/github.com/coreos.
  • It has cloned the project in that location, so now the source code of ETCD is available at $GOPATH/src/github.com/coreos/etcd.
  • Go get has cloned any repository that ETCD could need.
  • It has tried to install the project if it is not a library. This means, it has generated a binary file of ETCD and has put it in $GOPATH/bin folder.

By simply typing the go get [project] command, you'll get all that material from a project in your system. Then in your Go apps, you can just use any library by importing the path within the source. So for the ETCD project, it will be:

import "github.com/coreos/etcd" 

It's very important that you get familiar with the use of the Go get tool and stop using git clone when you want a project from a Git repository. This will save you some headaches when trying to import a project that isn't contained within your GOPATH.

Managing JSON data

JSON is the acronym for JavaScript Object Notation and, like the name implies, it's natively JavaScript. It has become very popular and it's the most used format for communication today. Go has very good support for JSON serialization/deserialization with the JSON package that does most of the dirty work for you. First of all, there are two concepts to learn when working with JSON:

  • Marshal: When you marshal an instance of a structure or object, you are converting it to its JSON counterpart.
  • Unmarshal: When you are unmarshaling some data, in the form of an array of bytes, you are trying to convert some JSON-expected-data to a known struct or object. You can also unmarshal to a map[string]interface{} in a fast but not very safe way to interpret the data as we'll see now.

Let's see an example of marshaling a string:

import ( 
"encoding/json" 
"fmt" 
) 
 
func main(){ 
    packt := "packt" 
    jsonPackt, ok := json.Marshal(packt) 
    if !ok { 
        panic("Could not marshal object")  
    }  
    fmt.Println(string(jsonPackt)) 
} 
$ "pack"

First, we have defined a variable called packt to hold the contents of the packt string. Then, we have used the json library to use the Marshal command with our new variable. This will return a new bytearray with the JSON and a flag to provide and boolOK result for the operation. When we print the contents of the bytes array (previous casting to string) the expected value appears. Note that packt appeared actually between quotes as the JSON representation would be.

The encoding package

Have you realized that we have imported the package encoding/json? Why is it prefixed with the word encoding? If you take a look at Go's source code to the src/encoding folder you'll find many interesting packages for encoding/decoding such as, XML, HEX, binary, or even CSV.

Now something a bit more complicated:

type MyObject struct { 
    Number int 
    `json:"number"` 
    Word string 
} 
 
func main(){ 
    object := MyObject{5, "Packt"} 
    oJson, _ := json.Marshal(object) 
    fmt.Printf("%s\n", oJson) 
} 
$ {"Number":5,"Word":"Packt"}

Conveniently, it also works pretty well with structures but what if I want to not use uppercase in the JSON data? You can define the output/input name of the JSON in the structure declaration:

type MyObject struct { 
    Number int 
    Word string 
} 
 
func main(){ 
    object := MyObject{5, "Packt"} 
    oJson, _ := json.Marshal(object) 
    fmt.Printf("%s\n", oJson) 
} 
$ {"number":5,"string":"Packt"}

We have not only lowercased the names of the keys, but we have even changed the name of the Word key to string.

Enough of marshalling, we will receive JSON data as an array of bytes, but the process is very similar with some changes:

type MyObject struct { 
Number int`json:"number"` 
Word string`json:"string"` 
} 
 
func main(){ 
    jsonBytes := []byte(`{"number":5, "string":"Packt"}`) 
    var object MyObject 
    err := json.Unmarshal(jsonBytes, &object) 
    if err != nil { 
        panic(err) 
    } 
    fmt.Printf("Number is %d, Word is %s\n", object.Number, object.Word) 
} 

The big difference here is that you have to allocate the space for the structure first (with a zero value) and the pass the reference to the method Unmarshal so that it tries to fill it. When you use Unmarshal, the first parameter is the array of bytes that contains the JSON information while the second parameter is the reference (that's why we are using an ampersand) to the structure we want to fill. Finally, let's use a generic map[string]interface{} method to hold the content of a JSON:

type MyObject struct { 
    Number int     `json:"number"` 
    Word string    `json:"string"` 
} 
 
func main(){ 
    jsonBytes := []byte(`{"number":5, "string":"Packt"}`) 
    var dangerousObject map[string]interface{} 
    err := json.Unmarshal(jsonBytes, &dangerousObject) 
    if err != nil { 
        panic(err) 
    } 
 
    fmt.Printf("Number is %d, ", dangerousObject["number"]) 
    fmt.Printf("Word is %s\n", dangerousObject["string"]) 
    fmt.Printf("Error reference is %v\n",  
dangerousObject["nothing"])
} 
$ Number is %!d(float64=5), Word is Packt 
Error reference is <nil> 

What happened in the result? This is why we described the object as dangerous. You can point to a nil location when using this mode if you call a non-existing key in the JSON. Not only this, like in the example, it could also interpret a value as a float64 when it is simply a byte, wasting a lot of memory.

So remember to just use map[string]interface{} when you need dirty quick access to JSON data that is fairly simple and you have under control the type of scenarios described previously.

Go tools

Go comes with a series of useful tools to ease the development process every day. Also in the golang page of GitHub, there are some tools that are supported by the Go team but they are not part of the compiler.

Most of the projects use tools such as gofmt so that all the code base looks similar. Godoc helps us to find useful information in Go's documentation and the goimport command to auto-import the packages we are using. Let's see them.

The golint tool

A linter analyzes source code to detect errors or improvements. The golint linter is available on https://github.com/golang/lint for installation (it doesn't come bundled with the compiler). It is very easy to use and is integrated some IDEs to be run when you save a source code file (Atom or Sublime Text, for example). Do you remember the implicit/explicit code that we run when talking about variables? Let's lint it:

//Explicitly declaring a "string" variable 
var explicit string = "Hello, I'm a explicitly declared variable" 
    
//Implicitly declaring a "string". 
Type inferred inferred := ", I'm an inferred variable " 

$ golint main.go

The main.go:10:21: command should omit the type string from the declaration of the explicitString variable; it will be inferred from the right-hand side.

It is telling us that Go compiler will actually infer this type of a variable from the code and you don't need to declare its type. What about the Train type on the interface section?

Type Train struct { 
    TrainWidth int 
} 

$ golint main.go

The main.go:5:6: type exported Train type should have a comment or remain not exported.

In this case, it's pointing us that a public type such as Train type must be commented so that users can read the generated documentation to know its behavior.

The gofmt tool

The gofmt tool comes bundled with the compiler that already has access to it. Its purpose is to provide a set of indentation, formatting, spacing and few other rules to achieve good-looking Go code. For example, let's take the code of Hello World and make it a bit weirder by inserting spaces everywhere:

package main 

func  main(){ 
    println("Hello World!") 
} 

$ gofmt main.go 
package main 
 
func main() { 
        println("Hello World!") 
} 

The gofmt command prints it correctly again. What is more, we can use the -w flag to overwrite the original file:

$ gofmt -w main.go

And now we'll have our file properly corrected.

The godoc tool

Go documentation is pretty extended and verbose. You can find detailed information about any topic you want to achieve. The godoc tool also helps you access this documentation directly from the command line. For example, we can query the package encoding/json:

$godoc cmd/encoding/json
[...]
FUNCTIONS
func Compact(dst *bytes.Buffer, src []byte) error
Compact appends to dst the JSON-encoded src with insignificant space
characters elided.
func HTMLEscape(dst *bytes.Buffer, src []byte)
[...]

You can also use grep, a bash utility for Linux and Mac, to find specific information about some functionality. For example, we'll use grep to look for text that mentions anything about parsing JSON files:

$ godoc cmd/encoding/json | grep parse

The Unmarshal command parses the JSON encoded data and stores the result in the object being parsed.

One of the things that the golint command warns about is to use the beginning of a comment with the same name of the function it describes. This way, if you don't remember the name of the function that parses JSON, you can use godoc with grep and search for parse so the beginning of the line will always be the function name like in the example preceding the Unmarshal command.

The goimport tool

The goimport tool is a must have in Go. Sometimes you remember your packages so well that you don't need to search much to remember their API but it's more difficult to remember the project they belong to when doing the import. The goimport command helps you by searching your $GOPATH for occurrences of a package that you could be using to provide you with the project import line automatically. This is very useful if you configure your IDE to run goimport on save so that all used packages in the source file are imported automatically if you used them. It also works the other way around--if you delete the function you were using from a package and the package isn't being used anymore, it will remove the import line.

Contributing to Go open source projects in GitHub

One important thing to mention about Go packaging system is that it needs to have a proper folder structure within the GOPATH. This introduces a small problem when working with GitHub projects. We are used to forking a project, cloning our fork and start working before committing the pull-request to the original project. Wrong!

When you fork a project, you create a new repository on GitHub within your username. If you clone this repository and start working with it, all new import references in the project will point to your repository instead of the original! Imagine the following case in the original repository:

package main 
import "github.com/original/a_library" 
[some code] 

Then, you make a fork and add a subfolder with a library called a_library/my_library that you want to use from the main package. The result is going to be the following:

package main 
import ( 
    "github.com/original/a_library" 
    "github.com/myaccount/a_library/my_library" 
) 

Now if you commit this line, the original repository that contains the code you have pushed will download this code anyways from your account again and it will use the references downloaded! Not the ones contained in the project!

So, the solution to this is simply to replace the git clone command with a go get pointing to the original library:

$ go get github.com/original/a_library
$ cd $GOPATH/src/github.com/original/a_library
$ git remote add my_origin https://github.com/myaccount/a_libbrary

With this modification, you can work normally in the original code without fear as the references will stay correct. Once you are done you just have to commit and push to your remote.

$ git push my_origin my_brach

This way, you can now access the GitHub web user interface and open the pull request without polluting the actual original code with references to your account.

Summary

After this first chapter, you must be familiar with the syntax of Go and some of the command-line tools that come bundled with the compiler. We have left apart concurrency capabilities for a later chapter as they are large and pretty complex to grasp at the beginning so that the reader learns the syntax of the language first, becomes familiar and confident with it, and then they can jump to understanding Communicating Sequential Processes (CSP) concurrency patterns and distributed applications. The next steps are to start with the creational design patterns.

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • A highly practical guide filled with numerous examples unleashing the power of design patterns with Go.
  • Discover an introduction of the CSP concurrency model by explaining GoRoutines and channels.
  • Get a full explanation, including comprehensive text and examples, of all known GoF design patterns in Go.

Description

Go is a multi-paradigm programming language that has built-in facilities to create concurrent applications. Design patterns allow developers to efficiently address common problems faced during developing applications. Go Design Patterns will provide readers with a reference point to software design patterns and CSP concurrency design patterns to help them build applications in a more idiomatic, robust, and convenient way in Go. The book starts with a brief introduction to Go programming essentials and quickly moves on to explain the idea behind the creation of design patterns and how they appeared in the 90’s as a common "language" between developers to solve common tasks in object-oriented programming languages. You will then learn how to apply the 23 Gang of Four (GoF) design patterns in Go and also learn about CSP concurrency patterns, the "killer feature" in Go that has helped Google develop software to maintain thousands of servers. With all of this the book will enable you to understand and apply design patterns in an idiomatic way that will produce concise, readable, and maintainable software.

Who is this book for?

The target audience is both beginner- and advanced-level developers in the Go programming language. No knowledge of design patterns is expected.

What you will learn

  • All basic syntax and tools needed to start coding in Go
  • Encapsulate the creation of complex objects in an idiomatic way in Go
  • Create unique instances that cannot be duplicated within a program
  • Understand the importance of object encapsulation to provide clarity and maintainability
  • Prepare cost-effective actions so that different parts of the program aren't affected by expensive tasks
  • Deal with channels and GoRoutines within the Go context to build concurrent application in Go in an idiomatic way
Estimated delivery fee Deliver to Greece

Premium delivery 7 - 10 business days

€17.95
(Includes tracking information)

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Feb 24, 2017
Length: 402 pages
Edition : 1st
Language : English
ISBN-13 : 9781786466204
Vendor :
Google
Category :
Languages :

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Estimated delivery fee Deliver to Greece

Premium delivery 7 - 10 business days

€17.95
(Includes tracking information)

Product Details

Publication date : Feb 24, 2017
Length: 402 pages
Edition : 1st
Language : English
ISBN-13 : 9781786466204
Vendor :
Google
Category :
Languages :

Packt Subscriptions

See our plans and pricing
Modal Close icon
€18.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
€189.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just €5 each
Feature tick icon Exclusive print discounts
€264.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just €5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total 120.97
.Go Programming Blueprints
€41.99
Building Microservices with Go
€36.99
Go Design Patterns
€41.99
Total 120.97 Stars icon
Banner background image

Table of Contents

10 Chapters
1. Ready... Steady... Go! Chevron down icon Chevron up icon
2. Creational Patterns - Singleton, Builder, Factory, Prototype, and Abstract Factory Design Patterns Chevron down icon Chevron up icon
3. Structural Patterns - Composite, Adapter, and Bridge Design Patterns Chevron down icon Chevron up icon
4. Structural Patterns - Proxy, Facade, Decorator, and Flyweight Design Patterns Chevron down icon Chevron up icon
5. Behavioral Patterns - Strategy, Chain of Responsibility, and Command Design Patterns Chevron down icon Chevron up icon
6. Behavioral Patterns - Template, Memento, and Interpreter Design Patterns Chevron down icon Chevron up icon
7. Behavioral Patterns - Visitor, State, Mediator, and Observer Design Patterns Chevron down icon Chevron up icon
8. Introduction to Gos Concurrency Chevron down icon Chevron up icon
9. Concurrency Patterns - Barrier, Future, and Pipeline Design Patterns Chevron down icon Chevron up icon
10. Concurrency Patterns - Workers Pool and Publish/Subscriber Design Patterns Chevron down icon Chevron up icon

Customer reviews

Top Reviews
Rating distribution
Full star icon Half star icon Empty star icon Empty star icon Empty star icon 1.8
(6 Ratings)
5 star 0%
4 star 16.7%
3 star 16.7%
2 star 0%
1 star 66.7%
Filter icon Filter
Top Reviews

Filter reviews by




Simone Gentili Sep 17, 2017
Full star icon Full star icon Full star icon Full star icon Empty star icon 4
Ogni pattern e' accompagnato da una serie di test. Non e' un libro sul TDD, quindi non si trovano tutti gli step della metodologia. Anzi, ... ci si concentra sopratutto sul design pattern. Mi sembra un ottimo testo. Sono solo ai primi capitoli ma mi sento gia' in dovere di consigliarlo. Mi hanno parlato di questo libro sullo slack ufficiale di go, nel canale #italy. Credo sia poco pubblicizzato, e che valga ogni singolo centesimo speso per acquistarlo.
Amazon Verified review Amazon
Amazon Customer Apr 10, 2017
Full star icon Full star icon Full star icon Empty star icon Empty star icon 3
The book is a good attempt at explaining the Gang of Four patterns using GO. Its main problem is the code examples; which are riddled with inconsistencies and errors. I would have given it a higher rating if this was not the case. The code examples that are supplied by Packt (the publisher) for download are correct though. It seems to me that the book was a rush job and it might have done with the editor giving it "the once over" before publishing. Great potential, hopefully fixed by 2nd edition.
Amazon Verified review Amazon
Brandon Bennett Apr 15, 2017
Full star icon Empty star icon Empty star icon Empty star icon Empty star icon 1
This book falls into the category of other poorly written OOP design programming books. It is riddled with errors and lacks a lot of reasoning and examples for each pattern. The examples used in the book are also the tragic abstract examples that have zero real world use. Instead of describing how a a design pattern could be used, it uses examples with objects Cars, or Tshirts which have fields like NumWheels, or Size. All of it is useless outside of the poor example in the bookOn the subject of patterns. The patterns presented are just the Gang of Four OOP patterns which are shoehorned into the Go language. They are not go idiomatic and a lot of the examples should not be used to write idiomatic code. If you are just learning Go this book does a poor job of describing the language and also teaches you how to be a OOP/Java programmer in a different language
Amazon Verified review Amazon
Patrick Jusic Dec 19, 2018
Full star icon Empty star icon Empty star icon Empty star icon Empty star icon 1
I just started reading the book and the largest part of examples are wrong... variables declared and not used (the intent was to call other variables), dublicate arguments call, function values return not specified.... Did they just try the code they wrote? I have doubts about it. One start because you cannot deliver a product like this. The star is for the author and for Packt too
Amazon Verified review Amazon
Houssem Jan 05, 2018
Full star icon Empty star icon Empty star icon Empty star icon Empty star icon 1
horrible
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the delivery time and cost of print book? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge? Chevron down icon Chevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order? Chevron down icon Chevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries: www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges? Chevron down icon Chevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live in Mexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live in Turkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order? Chevron down icon Chevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact customercare@packt.com with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at customercare@packt.com using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy? Chevron down icon Chevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on customercare@packt.com with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on customercare@packt.com within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on customercare@packt.com who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on customercare@packt.com within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged? Chevron down icon Chevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use? Chevron down icon Chevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela