39,59 €
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.
Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:
Seitenzahl: 499
Veröffentlichungsjahr: 2017
Copyright © 2017 Packt Publishing
All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.
Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing, and its dealers and distributors will be held liable for any damages caused or alleged to be caused directly or indirectly by this book.
Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.
First published: February 2017
Production reference: 1170217
Published by Packt Publishing Ltd.
Livery Place
35 Livery Street
Birmingham
B3 2PB, UK.
ISBN 978-1-78646-620-4
www.packtpub.com
Author
Mario Castro Contreras
Copy Editor
Safis Editing
Reviewer
Shiju Varghese
Project Coordinator
Izzat Contractor
Commissioning Editor
Kunal Parikh
Proofreader
Safis Editing
Acquisition Editor
Chaitanya Nair
Indexer
Mariammal Chettiyar
Content Development Editor
Zeeyan Pinheiro
Graphics
Abhinash Sahu
Technical Editor
Pavan Ramchandani
Production Coordinator
Deepika Naik
Mario Castro Contreras is a software engineer who has specialized in distributed systems and big data solutions. He works as a site reliability engineer, and now he is focused on containerized solutions and apps using most of Google Cloud suite; especially, Kubernetes. He has a wide experience in systems and solutions integration, and he has written many scalable and reliable 12 factor apps using Go and Docker. He has designed Big Data architectures for financial services and media, and he has written data processing pipelines using event-driven architectures written purely in Go. He is also very active in the open source community, and you can find him on his GitHub account with the username sayden. In the past, he has also written mobile applications and backends in Java.
Mario is passionate about programming languages, and he found the best balance between fun and productivity in Go; however, recently, he enjoys writing in Rust and embedded systems in C. He is also passionate about road cycling and winter sports.
I'd like to express my deep gratitude to my parents for supporting me in my journey through computers since I was 8. To Urszula, Tyrion and Tesla for their daily support and for being with me in the long nights writing this book.
I'd like to thank Chaitanya, for her guidance at the beginning of the book, Zeeyan, for his patience and help on every chapter, and Pavan, for the help and explanations. But also to all the reviewers, especially to Shiju, and the entire team at Packt that made this book possible.
Shiju Varghese is a solutions architect focused on building highly scalable cloud native applications with a special interest in APIs, microservices, containerized architectures, and distributed systems. He currently specializes in Go, Google Cloud, and container technologies. He is an early adopter of the Go programming language and provides consultation and training for building backend systems and microservices with Go ecosystem. He has been a mentor to various start-ups and enterprises for the technology transformation to Go. He has been a speaker at numerous technology conferences, including GopherCon India.
Shiju has authored two books on Go, titled Web Development with Go and Go Recipes, both published by Apress.
For support files and downloads related to your book, please visit www.PacktPub.com.
Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at www.PacktPub.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at [email protected] more details.
At www.PacktPub.com, you can also read a collection of free technical articles, sign up for a range of free newsletters and receive exclusive discounts and offers on Packt books and eBooks.
https://www.packtpub.com/mapt
Get the most in-demand software skills with Mapt. Mapt gives you full access to all Packt books and video courses, as well as industry-leading tools to help you plan your personal development and advance your career.
Thanks for purchasing this Packt book. At Packt, quality is at the heart of our editorial process. To help us improve, please leave us an honest review on this book's Amazon page at https://www.amazon.com/dp/1786466201.
If you'd like to join our team of regular reviewers, you can email us at [email protected]. We award our regular reviewers with free eBooks and videos in exchange for their valuable feedback. Help us be relentless in improving our products!
Welcome to the book Go Design Patterns! With this book, you'll learn basic and advanced techniques and patterns with the Go language. Don't worry if you have never written Go code before; this book will gradually introduce you to the various concepts in Go programming. At the same time, experts will find many tips and tricks on the language, so I encourage you to not miss any chapter. If you already know the classic design patterns, you'll find this book very handy, not only as a reference book but also as a way to learn idiomatic Go approaches to solve common problems that you may already know.
The book is divided in three sections:
The book will slowly raise the difficulty of some tasks. We have explained tips and tricks in every chapter.
Chapter 1, Ready… Steady…Go!, attempts to help newcomers to the Go programming language who have some background in any other programming language. It will begin by showing how to install the Go environment in a Linux machine, moving to syntax, type and flow control.
Chapter 2, Creational Patterns - Singleton, Builder, Factory, Prototype, and Abstract Factory Design Patterns, introduces the problems that can arise when an object creation or management is particularly complex or expensive using the Singleton, Builder, Factory, and Abstract Factory design patterns.
Chapter 3, Structural Patterns - Composite, Adapter, and Bridge Design Patterns, deals with the first set of Structural patterns about object composition to get some new functionality. Such as creating an intermediate object and using of various objects as if there is only one.
Chapter 4, Structural Patterns - Proxy, Facade, Decorator, and Flyweight Design Patterns, is less oriented to multi-object composition but focuses more on obtaining new functionality in existing objects. The Decorator pattern is commonly used to follow the open-closed principle. Facade is extensively used in API’s where you want a single source for many sources of information and actions. Flyweight is not so common but it’s a very useful pattern when the memory is becoming a problem caused by a large collection of similar objects. Finally, the Proxy pattern wraps on an object to provide the same functionality, but at the same time, adding something to the proxy’s functionality.
Chapter 5, Behavioral patterns - Strategy, Chain of Responsibility, Command, and Mediator Design Patterns, deals with the first behavioral pattern to make objects react in an expected or bounded way. We’ll start with the Strategy pattern, perhaps the most important design pattern in object-oriented programming, as many design patterns have something in common with it. Then we’ll move to the Chain of Responsibility to build chains of objects that can decide which between them must deal with a particular case. Finally, Command pattern to encapsulate actions that don’t necessarily need to be executed immediately or must be stored.
Chapter 6, Behavioral Patterns - Template, Memento, and Interpreter Design Patterns, continues with Behavioral patterns introducing the Interpreter pattern, a quite complex pattern to create small languages and Interpreters for them. It can be very useful when a problem can be solved by inventing a small language for it. The Memento pattern is in front of our eyes every day with the Undo button in apps. The Template pattern helps developers by defining an initial structure of an operation so that the final users of the code can finish it.
Chapter 7, Behavioral Patterns - Visitor, State, Mediator, and Observer Design Patterns, depicts the Observer pattern, an important pattern that is becoming tremendously popular in distributed systems and reactive programming. The Visitor pattern deals with complex hierarchies of objects where you need to apply a particular action depending on the object. Finally, the State pattern is commonly used in video games and finite state machines and allows an object to change its behavior depending on its own state.
Chapter 8, Introduction to Go's Concurrency, explains with more detail the CSP concurrency model used in Go by going through some examples using Goroutines and channels, as well as mutexes and syncs.
Chapter 9, Concurrency Patterns - Barrier, Future, and Pipeline Design Patterns, will introduce some of the CSP concurrency patterns that are idiomatic to the Go language by walking through some examples and explanations. These are small but really powerful patterns so we will provides a few examples of the use of each of them, as well as some schemas (if possible) that will make the understanding of each of them easier.
Chapter 10, Concurrency Patterns - Workers Pool, and Publish or Subscriber Design Patterns, talks about a couple of patterns with concurrent structures. We will explain every step in detail so you can follow the examples carefully. The idea is to learn patterns to design concurrent applications in idiomatic Go. We are using channels and Goroutines heavily, instead of locks or sharing variables.
Most of the chapters in this book are written following a simple TDD approach, here the requirements are written first, followed by some unit tests and finally the code that satisfies those requirements. We will use only tools that comes with the standard library of Go as a way to better understand the language and its possibilities. This idea is key to follow the book and understanding the way that Go solves problems, especially in distributed systems and concurrent applications.
This book is for both beginners and advanced-level developers in the Go programming language. No knowledge of design patterns is expected.
Feedback from our readers is always welcome. Let us know what you think about this book-what you liked or disliked. Reader feedback is important for us as it helps us develop titles that you will really get the most out of. To send us general feedback, simply e-mail [email protected], and mention the book's title in the subject of your message. If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, see our author guide at www.packtpub.com/authors.
Now that you are the proud owner of a Packt book, we have a number of things to help you to get the most from your purchase.
You can download the example code files for this book from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
You can download the code files by following these steps:
Once the file is downloaded, please make sure that you unzip or extract the folder using the latest version of:
The code bundle for the book is also hosted on GitHub at https://github.com/PacktPublishing/Go-Design-Patterns. We also have other code bundles from our rich catalog of books and videos available at https://github.com/PacktPublishing/. Check them out!
Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you find a mistake in one of our books-maybe a mistake in the text or the code-we would be grateful if you could report this to us. By doing so, you can save other readers from frustration and help us improve subsequent versions of this book. If you find any errata, please report them by visiting http://www.packtpub.com/submit-errata, selecting your book, clicking on the Errata Submission Form link, and entering the details of your errata. Once your errata are verified, your submission will be accepted and the errata will be uploaded to our website or added to any list of existing errata under the Errata section of that title.
To view the previously submitted errata, go to https://www.packtpub.com/books/content/support and enter the name of the book in the search field. The required information will appear under the Errata section.
Piracy of copyrighted material on the Internet is an ongoing problem across all media. At Packt, we take the protection of our copyright and licenses very seriously. If you come across any illegal copies of our works in any form on the Internet, please provide us with the location address or website name immediately so that we can pursue a remedy.
Please contact us at [email protected] with a link to the suspected pirated material.
We appreciate your help in protecting our authors and our ability to bring you valuable content.
If you have a problem with any aspect of this book, you can contact us at [email protected], and we will do our best to address the problem.
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...?
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:
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.
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.
To install Go in Linux you have two options:
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.
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.
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.gzsudo mv go /usr/local/goOn 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/binWith -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/binCheck that our go/bin directory is available:
$ go versionGo version go1.6.2 linux/amd64To 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.
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 versionGo version go1.6.2 darwin/amd64If 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.
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/goThe $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}/goSave 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/goIf the output of the preceding command points to your chosen Go path, everything is correct and you can continue to write your first program.
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:
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.goHello 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_worldNothing 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_worldHello World!And our message appeared! In Windows, you just need to type the name of the .exe file to get the same result.
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.
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:
But you can also find Go plugins for:
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 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:
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.
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.
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 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.
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 }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.
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.
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:
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.
