Book Image

Go Design Patterns

By : Mario Castro Contreras
Book Image

Go Design Patterns

By: Mario Castro Contreras

Overview of this book

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.
Table of Contents (17 chapters)
Go Design Patterns
Credits
About the Author
About the Reviewer
www.PacktPub.com
Customer Feedback
Preface

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.