Castro Contreras | Go Design Patterns | E-Book | www.sack.de
E-Book

E-Book, Englisch, 402 Seiten

Castro Contreras Go Design Patterns

Best practices in software development and CSP
1. Auflage 2025
ISBN: 978-1-78646-190-2
Verlag: De Gruyter
Format: PDF
Kopierschutz: Adobe DRM (»Systemvoraussetzungen)

Best practices in software development and CSP

E-Book, Englisch, 402 Seiten

ISBN: 978-1-78646-190-2
Verlag: De Gruyter
Format: PDF
Kopierschutz: Adobe DRM (»Systemvoraussetzungen)



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.

Castro Contreras Go Design Patterns jetzt bestellen!

Autoren/Hrsg.


Weitere Infos & Material


Chapter 2. Creational Patterns - Singleton, Builder, Factory, Prototype, and Abstract Factory Design Patterns


We have defined two types of cars-luxury and family. The car Factory will have to return The first groups of design patterns that we are going to cover are the Creational patterns. As the name implies, it groups common practices for creating objects, so object creation is more encapsulated from the users that need those objects. Mainly, creational patterns try to give ready-to-use objects to users instead of asking for their creation, which, in some cases, could be complex, or which would couple your code with the concrete implementations of the functionality that should be defined in an interface.

Singleton design pattern - having a unique instance of a type in the entire program


Have you ever done interviews for software engineers? It's interesting that when you ask them about design patterns, more than 80% will mention Singleton design pattern. Why is that? Maybe it's because it is one of the most used design patterns out there or one of the easiest to grasp. We will start our journey on creational design patterns because of the latter reason.

Description


The Singleton pattern is easy to remember. As the name implies, it will provide you with a single instance of an object, and guarantee that there are no duplicates.

At the first call to use the instance, it is created and then reused between all the parts in the application that need to use that particular behavior.

You'll use the Singleton pattern in many different situations. For example:

  • When you want to use the same connection to a database to make every query
  • When you open a Secure Shell (SSH) connection to a server to do a few tasks, and don't want to reopen the connection for each task
  • If you need to limit the access to some variable or space, you use a Singleton as the door to this variable (we'll see in the following chapters that this is more achievable in Go using channels anyway)
  • If you need to limit the number of calls to some places, you create a Singleton instance to make the calls in the accepted window

The possibilities are endless, and we have just mentioned some of them.

Objectives


As a general guide, we consider using the Singleton pattern when the following rule applies:

  • We need a single, shared value, of some particular type.
  • We need to restrict object creation of some type to a single unit along the entire program.

Example - a unique counter


As an example of an object of which we must ensure that there is only one instance, we will write a counter that holds the number of times it has been called during program execution. It shouldn't matter how many instances we have of the counter, all of them must the same value and it must be consistent between the instances.

Requirements and acceptance criteria


There are some requirements and acceptance criteria to write the described single counter. They are as follows:

  • When no counter has been created before, a new one is created with the value 0
  • If a counter has already been created, return this instance that holds the actual count
  • If we call the method , the count must be incremented by 1

We have a scenario with three tests to check in our unit tests.

Writing unit tests first


Go's implementation of this pattern is slightly different from what you'll find in pure object-oriented languages such as Java or C++, where you have static members. In Go, there's nothing like static members, but we have package scope to deliver a similar result.

To set up our project, we must create a new folder within our directory. The general rule as we mentioned in the Chapter 1, , is to create a subfolder with the VCS provider (such as GitHub), the username, and the name of the project.

For example, in my case, I use GitHub as my VCS and my username is so I will create the path . The  instance in the path is the project name, the creational subfolder will also be our library name, and singleton the name of this particular package and subfolder:

mkdir -p $GOPATH/src/github.com/sayden/go-design-patterns/creational/singleton cd $GOPATH/src/github.com/sayden/go-design- patterns/creational/singleton

Create a new file inside the singleton folder called to also reflect the name of the package and write the following package declarations for the type:

package singleton type Singleton interface { AddOne() int } type singleton struct { count int } var instance *singleton func GetInstance() Singleton { return nil } func (s *singleton) AddOne() int { return 0 }

As we are following a TDD approach while writing the code, let's code the tests that use the functions we have just declared. The tests are going to be defined by following the acceptance criteria that we have written earlier. By convention in test files, we must create a file with the same name as the file to test, suffixed with . Both must reside in the same folder:

package singleton import "testing" func TestGetInstance(t *testing.T) { counter1 := GetInstance() if counter1 == nil { //Test of acceptance criteria 1 failed t.Error("expected pointer to Singleton after calling GetInstance(), not nil") } expectedCounter := counter1 }

The first test checks something obvious, but no less important, in complex applications. We actually receive something when we ask for an instance of the counter. We have to think of it as a Creational pattern--we delegate the creation of the object to an unknown package that could fail in the creation or retrieval of the object. We also store the current counter in the  variable to make a comparison later:

currentCount := counter1.AddOne() if currentCount != 1 { t.Errorf("After calling for the first time to count, the count must be 1 but it is %d\n", currentCount) }

Now we take advantage of the zero-initialization feature of Go. Remember that integer types in Go cannot be nil and as we know, that this is the first call to the counter, and it is an integer type of variable, and we also know that it is zero-initialized. So after the first call to the  function, the value of the count must be 1.

The test that checks the second condition proves that the  variable is not different to the returned connection that we requested later. If they were different, the message will cause the test to fail:

counter2 := GetInstance() if counter2 != expectedCounter { //Test 2 failed t.Error("Expected same instance in counter2 but it got a different instance") }

The last test is simply counting 1 again with the second instance. The previous result was 1, so now it must give us 2:

currentCount = counter2.AddOne() if currentCount != 2 { t.Errorf("After calling 'AddOne' using the second counter, the current count must be 2 but was %d\n", currentCount) }

The last thing we have to do to finish our test part is to execute the tests to make sure that they are failing before implementation. If one of them doesn't fail, it implies that we have done something wrong, and we have to reconsider that particular test. We must open the terminal and navigate to the path of the singleton package to execute:

$ go test -v . === RUN TestGetInstance --- FAIL: TestGetInstance (0.00s) singleton_test.go:9: expected pointer to Singleton after calling GetInstance(), not nil singleton_test.go:15: After calling for the first time to count, the count must be 1 but it is 0 singleton_test.go:27: After calling 'AddOne' using the second counter, the current count must be 2 but was 0 FAIL exit status 1 FAIL

Implementation


Finally, we have to implement the Singleton pattern. As we mentioned earlier, we'll usually write a method and instance to retrieve the Singleton instance in languages such as Java or C++. In Go, we don't have the keyword , but we can achieve the same result by using the scope of the package. First, we create a that contains the object which we want to guarantee to be a Singleton during the execution of the program:

package creational type singleton struct{ count int } var instance *singleton func GetInstance() *singleton { if instance == nil { instance = new(singleton) } return instance } func (s *singleton)...


Castro Contreras Mario :

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 broad 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 back ends in Java. Mario is passionate about programming languages, and he found the best balance between fun and productivity in Go; however, recently he has enjoyed writing in Rust and embedded systems in C. He is also passionate about road cycling and winter sports.



Ihre Fragen, Wünsche oder Anmerkungen
Vorname*
Nachname*
Ihre E-Mail-Adresse*
Kundennr.
Ihre Nachricht*
Lediglich mit * gekennzeichnete Felder sind Pflichtfelder.
Wenn Sie die im Kontaktformular eingegebenen Daten durch Klick auf den nachfolgenden Button übersenden, erklären Sie sich damit einverstanden, dass wir Ihr Angaben für die Beantwortung Ihrer Anfrage verwenden. Selbstverständlich werden Ihre Daten vertraulich behandelt und nicht an Dritte weitergegeben. Sie können der Verwendung Ihrer Daten jederzeit widersprechen. Das Datenhandling bei Sack Fachmedien erklären wir Ihnen in unserer Datenschutzerklärung.