36,59 €
Explore various dependency injection methods in Go such as monkey patching, constructor injection, and method injection
Key Features
Book Description
Hands-On Dependency Injection in Go takes you on a journey, teaching you about refactoring existing code to adopt dependency injection (DI) using various methods available in Go.
Of the six methods introduced in this book, some are conventional, such as constructor or method injection, and some unconventional, such as just-in-time or config injection. Each method is explained in detail, focusing on their strengths and weaknesses, and is followed with a step-by-step example of how to apply it. With plenty of examples, you will learn how to leverage DI to transform code into something simple and flexible. You will also discover how to generate and leverage the dependency graph to spot and eliminate issues. Throughout the book, you will learn to leverage DI in combination with test stubs and mocks to test otherwise tricky or impossible scenarios.
Hands-On Dependency Injection in Go takes a pragmatic approach and focuses heavily on the code, user experience, and how to achieve long-term benefits through incremental changes.
By the end of this book, you will have produced clean code that's easy to test.
What you will learn
Who this book is for
Hands-On Dependency Injection in Go is for programmers with a few year s experience in any language and a basic understanding of Go. If you wish to produce clean, loosely coupled code that is inherently easier to test, this book is for you.
Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:
Seitenzahl: 446
Veröffentlichungsjahr: 2018
Copyright © 2018 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 or its dealers and distributors, will be held liable for any damages caused or alleged to have been 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.
Commissioning Editor: Aaron LazarAcquisition Editor: Shriram ShekharContent Development Editor: Akshada IyerTechnical Editor: Riddesh DawneCopy Editor:Safis EditingProject Coordinator: Prajakta NaikProofreader: Safis EditingIndexer: Tejal Daruwale SoniGraphics: Jisha ChirayilProduction Coordinator: Jyoti Chauhan
First published: November 2018
Production reference: 1231118
Published by Packt Publishing Ltd. Livery Place 35 Livery Street Birmingham B3 2PB, UK.
ISBN 978-1-78913-276-2
www.packtpub.com
To my wife, May; this book would be far less without your help and support, as would I. - Corey Scott
Mapt is an online digital library that gives you full access to over 5,000 books and videos, as well as industry leading tools to help you plan your personal development and advance your career. For more information, please visit our website.
Spend less time learning and more time coding with practical ebooks and videos from over 4,000 industry professionals
Improve your learning with Skill Plans built especially for you
Get a free eBook or video every month
Mapt is fully searchable
Copy and paste, print, and bookmark content
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.packt.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] for more details.
At www.packt.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.
Corey Scott is a senior software engineer currently living in Melbourne, Australia. He's been programming professionally since 2000, with the last 5 years spent building large-scale distributed services in Go. An occasional technical speaker and blogger on a variety of software-related topics, he is passionate about designing and building quality software. He believes that software engineering is a craft that should be honed, debated, and continuously improved. He takes a pragmatic, non-zealot approach to coding and is always up for a good debate about software engineering, continuous delivery, testing, or clean coding.
Ryan Law is a software engineer who enjoys spending time understanding how the world around him works. Being an early engineer in Grab, he has experience of working with many different technologies at scale, from building web portals, to designing distributed services in Go, to managing all of Grab's cloud infrastructure.
If you're interested in becoming an author for Packt, please visit authors.packtpub.com and apply today. We have worked with thousands of developers and tech professionals, just like you, to help them share their insight with the global tech community. You can make a general application, apply for a specific hot topic that we are recruiting an author for, or submit your own idea.
Title Page
Copyright and Credits
Hands-On Dependency Injection in Go
Dedication
About Packt
Why subscribe?
Packt.com
Contributors
About the author
About the reviewer
Packt is searching for authors like you
Preface
Who this book is for
What this book covers
To get the most out of this book
Download the example code files
Download the color images
Conventions used
Get in touch
Reviews
Never Stop Aiming for Better
Technical requirements
Why does DI matter?
So, how do I define DI?
Code smells that indicate you might need DI
Code bloat
Resistance to change
Wasted effort
Tight coupling
Healthy skepticism
A quick word about idiomatic Go
Leave your baggage at the door
Summary
Questions
Further reading
SOLID Design Principles for Go
Technical requirements
Single responsibility principle (SRP)
How does this relate to DI?
What does this mean for Go?
Go interfaces, structs, and functions
Go packages
Open/closed principle (OCP)
How does this relate to DI?
What does this mean for Go?
Liskov substitution principle (LSP)
How does this relate to DI?
What does this mean for Go?
Interface segregation principle (ISP)
How does this relate to DI?
What does this mean for Go?
Dependency inversion principle (DIP)
How does this relate to DI?
What does this mean for Go?
Summary
Questions
Further reading
Coding for User Experience
Technical requirements
Optimizing for humans
What does user experience mean for Go code?
Start with simple – get complicated only when you must
Apply just enough abstraction
Follow industry, team, and language conventions
Export only what you must
Aggressively apply the single responsibility principle
Discovering a good user experience
Who is the user?
What are your users capable of?
Why do users want to use your code?
How do they expect to use it?
When to compromise
Final thoughts on coding for user experience
A security blanket named unit tests
So why do I write unit tests?
What should I test?
Table-driven tests
Stubs
Mocks
Test-induced damage
Warning signs of test-induced damage
Parameters, config options, or outputs that only exist because of tests
Parameters that cause or are caused by leaky abstractions
Publishing mocks in production code
Excessive test coverage
Visualizing your package dependencies with Godepgraph
Installing the tools
Generating a dependency graph
Interpreting the dependency graph
Summary
Questions
Introduction to the ACME Registration Service
Technical requirements
Goals for our system
High readability
High testability
Low coupling
Final thoughts on goals
Introduction to our system
Software architecture
Known issues
Testability
Duplication of effort
Lack of isolation in tests
High coupling between the data and REST packages
High coupling with the config package
Downstream currency service
Summary
Questions
Dependency Injection with Monkey Patching
Technical requirements
Monkey magic!
Advantages of monkey patching
Applying monkey patching
Introducing SQLMock
Monkey patching with SQLMock
Testing error handling
Reducing test bloat with table-driven tests
Monkey patching between packages
When the magic fades
Summary
Questions
Further reading
Dependency Injection with Constructor Injection
Technical requirements
Constructor injection
Addressing the duck in the room
Advantages of constructor injection
Applying constructor injection
Decoupling from the dependency
Building the constructor
Improving test scenario coverage
Validating our improvements with the dependency graph
Disadvantages of constructor injection
Summary
Questions
Dependency Injection with Method Injection
Technical requirements
Method injection
Advantages of method injection
Applying method injection
A quick recap
Stopping short
Applying method injection to the data package
Applying method injection to the exchange package
Applying method injection to the model layer (the Get, List, and Register packages)
Applying the method injection of context to the REST package
Latency budgets
Disadvantages of method injection
Summary
Questions
Dependency Injection by Config
Technical requirements
Config injection
Advantages of config injection
Applying config injection
Applying config injection to the model layer
Applying config injection to the data package
Applying config injection to the exchange package
Boundary tests
Disadvantages of config injection
Summary
Questions
Just-in-Time Dependency Injection
Technical requirements
JIT injection
Advantages of JIT injection
Applying JIT injection
Private dependencies
Unit test coverage
Coverage and dependency graph
Chasing away the monkeys
Optional public dependencies
Disadvantages of JIT injection
Summary
Questions
Off-the-Shelf Injection
Technical requirements
Off-the-shelf injection with Wire
Introducing providers
Understanding injectors
Adopting provider sets
Advantages of off-the-shelf injection
Applying off-the-shelf injection
Adopting Google Wire
API regression tests
Disadvantages of off-the-shelf injection
Summary
Questions
Curb Your Enthusiasm
Technical requirements
DI induced damage
A long constructor parameter list
Injecting an object when config would do
Needless indirection
Service locator
Premature future-proofing
Mocking HTTP requests
Mocking an HTTP request with DI
Mocking HTTP requests with config
Unnecessary injection
Summary
Questions
Reviewing Our Progress
Technical requirements
Overview of the improvements
Global singletons
High coupling with the config package
Removing the dependence on upstream service
Stopping short and latency budgets
Simplified dependency creation
Coupling and extensibility
Review of the dependency graph
Review of test coverage and testability
Test coverage
Starting a new service with DI
The user experience
Code structure
Cross-cutting concerns
Designing from outside-in
Summary
Questions
Assessment
Chapter 1, Never Stop Aiming for Better
Chapter 2, SOLID Design Principles for Go
Chapter 3, Coding for User Experience
Chapter 4, Introduction to the ACME Registration Service
Chapter 5, Dependency Injection with Monkey Patching
Chapter 6, Dependency Injection with Constructor Injection
Chapter 7, Dependency Injection with Method Injection
Chapter 8, Dependency Injection by Config
Chapter 9, Just-in-Time Dependency Injection
Chapter 10, Off-the-Shelf Injection
Chapter 11, Curbing Your Enthusiasm
Chapter 12, Reviewing Our Progress
Other Books You May Enjoy
Leave a review - let other readers know what you think
Howdy! This book intends to be a hands-on introduction to dependency injection with Go. It may surprise you to learn that there are many different methods for applying dependency injection available in the Go language and, in this book, we will discuss six different and occasionally complementary options.
Dependency injection, like many software engineering concepts, is easily and often misunderstood, so this text seeks to address that. It delves into related concepts, such as the principles of SOLID, code smells, and test-induced damage, so as to offer a broader and more practical view.
The aim of Hands-On Dependency Injection in Go is not only to teach you how to apply dependency injection, but also when, where, and when not to. Each of the methods is clearly defined; we discuss its advantages and disadvantages, and when the method is best applied. Also, each method is applied step by step using significant examples.
As much as I love dependency injection, it's not always the right tool for the job. This book will also help you spot situations where applying dependency injection is perhaps not the best option.
As each dependency injection method is introduced, I would ask you to pause for a moment, step back, and consider the following. What problem is the technique trying to resolve? And what would your code look like after you apply this method? Don't worry if the answers to these questions don't come quickly; by the end of the book, they will.
Happy coding!
This book is designed for developers who wish that their code was easy to read, test, and maintain. It is intended for developers coming from an object-oriented background who want to get more out of Go, as well as for developers who believe that quality code is about more than delivering one particular feature.
After all, writing code is easy. Similarly, getting a single test case to pass is simple. Creating code whose tests continue to pass after months or years of adding additional features is heading toward the impossible.
For us to be able to deliver code at that level consistently, we require a lot of nifty tricks. This book hopes to not only equip you with those tricks, but also to give you the wisdom to apply them effectively.
Chapter 1, Never Stop Aiming for Better, aims to define dependency injection, outline why dependency injection is important for Go development, and introduce several code smells that may be addressed with dependency injection.
Chapter 2, SOLID Design Principles for Go, introduces the SOLID software design principles and how they relate to both dependency injection and programming in Go.
Chapter 3, Coding for User Experience, addresses often overlooked concepts in programming, namely testing and the code's user experience. It also introduces many other concepts, including mocks, stubs, test-induced damage and the dependency graph, that we will use throughout the book.
Chapter 4, Introduction to the ACME Registration Service, introduces a small, fake service that forms the basis for many of our examples in later chapters. It highlights the issues with the service's current implementation and outlines the goals we are hoping to achieve by applying dependency injection.
Chapter 5, Dependency Injection with Monkey Patching, examines monkey patching as a way to swap out dependencies during our tests. This chapter applies monkey patching to our sample service to decouple our tests from the database, and to decouple the different layers from each other, all without resorting to significant refactoring.
Chapter 6, Dependency Injection with Constructor Injection, introduces perhaps the most traditional form of dependency injection – constructor injection. This chapter will examine its many advantages, its disadvantages, and show how to successfully apply constructor injection.
Chapter 7, Dependency Injection with Method Injection, introduces the second most common form of dependency injection – method injection. This chapter discusses the advantages and disadvantages of method injection and shows how to successfully apply the method for request-scoped dependencies.
Chapter 8, Dependency Injection by Config, introduces config injection. Config injection is an extension of constructor and method injection that intends to improve the usability of the code by reducing the number of parameters.
Chapter 9, Just-in-Time Dependency Injection, discusses another unusual form of dependency injection – just-in-time injection. Just-in-time (JIT) injection is a strategy that gives us many of the benefits of dependency injection, such as decoupling and testability, without adding parameters to our constructors or methods.
Chapter 10, Off-the-Shelf Injection, introduces the final dependency injection method – dependency injection using a framework. This chapter outlines the advantages and disadvantages related to adopting a dependency injection framework and also introduces and applies Google Go Cloud's wire framework to our sample service.
Chapter 11, Curb Your Enthusiasm, examines some of the ways in which dependency injection can go wrong. It offers many examples where applying dependency injection is either unnecessary or detrimental to the code.
Chapter 12, Reviewing Our Progress, contrasts the state of our sample service after applying dependency injection with the state it was in when it was introduced. It also discusses the steps we could have taken if we were starting a new service with dependency injection.
While dependency injection and many of the other programming concepts discussed in this book are not simple or intuitive, this book introduces them with little assumed knowledge.
That said, the following is assumed:
You have a basic level of experience with building and testing Go code.
You are comfortable with the idea of objects/classes due to prior experience with Go or an object-oriented language, such as Java or Scala.
Additionally, it would be beneficial to have at least a passing understanding of building and consuming HTTP-based REST APIs. In Chapter 4, Introduction to the ACME Registration Service, we will introduce an example REST service that will form the basis for many of the examples in the book. To be able to run this sample service, you will need to be able to install and configure a MySQL database service on your development environment and be able to customize the supplied configuration to match your local environment. All of the commands provided in this book were developed and tested under OSX and should work without modification on any Linux- or Unix-based system. Developers with Windows-based development environments will need to adjust the commands before running them.
You can download the example code files for this book from your account at www.packt.com. If you purchased this book elsewhere, you can visit www.packt.com/support and register to have the files emailed directly to you.
You can download the code files by following these steps:
Log in or register at
www.packt.com
.
Select the
SUPPORT
tab.
Click on
Code Downloads & Errata
.
Enter the name of the book in the
Search
box and follow the onscreen instructions.
Once the file is downloaded, please make sure that you unzip or extract the folder using the latest version of:
WinRAR/7-Zip for Windows
Zipeg/iZip/UnRarX for Mac
7-Zip/PeaZip for Linux
The code bundle for the book is also hosted on GitHub at https://github.com/PacktPublishing/Hands-On-Dependency-Injection-in-Go. In case there's an update to the code, it will be updated on the existing GitHub repository.
We also have other code bundles from our rich catalog of books and videos available at https://github.com/PacktPublishing/. Check them out!
We also provide a PDF file that has color images of the screenshots/diagrams used in this book. You can download it here: http://www.packtpub.com/sites/default/files/downloads/Bookname_ColorImages.pdf.
There are a number of text conventions used throughout this book.
CodeInText: Indicates code words in text, database table names, folder names, filenames, file extensions, pathnames, dummy URLs, user input, and Twitter handles. Here is an example: "Mount the downloaded WebStorm-10*.dmg disk image file as another disk in your system."
A block of code is set as follows:
html, body, #map { height: 100%; margin: 0; padding: 0}
When we wish to draw your attention to a particular part of a code block, the relevant lines or items are set in bold:
[default]exten => s,1,Dial(Zap/1|30)exten => s,2,Voicemail(u100)
exten => s,102,Voicemail(b100)
exten => i,1,Voicemail(s0)
Any command-line input or output is written as follows:
$ mkdir css
$ cd css
Bold: Indicates a new term, an important word, or words that you see on screen. For example, words in menus or dialog boxes appear in the text like this. Here is an example: Select System info from the Administration panel."
Feedback from our readers is always welcome.
General feedback: If you have questions about any aspect of this book, mention the book title in the subject of your message and email us at [email protected].
Errata: Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you have found a mistake in this book, we would be grateful if you would report this to us. Please visit www.packt.com/submit-errata, selecting your book, clicking on the Errata Submission Form link, and entering the details.
Piracy: If you come across any illegal copies of our works in any form on the internet, we would be grateful if you would provide us with the location address or website name. Please contact us at [email protected] with a link to the material.
If you are interested in becoming an author: If there is a topic that you have expertise in, and you are interested in either writing or contributing to a book, please visit authors.packtpub.com.
Please leave a review. Once you have read and used this book, why not leave a review on the site that you purchased it from? Potential readers can then see and use your unbiased opinion to make purchase decisions, we at Packt can understand what you think about our products, and our authors can see your feedback on their book. Thank you!
For more information about Packt, please visit packt.com.
Do you want code that is easier to maintain? How about easier to test? Easier to extend? Dependency Injection (DI) might be just the tool you need.
In this chapter, we will define DI, perhaps in a somewhat atypical way, and explore the code smells that could indicate you need DI. We will also talk briefly about Go and how I would like you to approach the ideas presented in this book.
Are you ready to join me on a journey to better Go code?
We will cover the following topics:
Why does DI matter?
What is DI?
When should I apply DI?
How can I improve as a Go programmer?
Hopefully, you will have Go installed. It is downloadable from https://golang.org/ or your preferred package manager.
All code in this chapter is available at https://github.com/PacktPublishing/Hands-On-Dependency-Injection-in-Go/tree/master/ch01.
As professionals, we should never stop learning. Learning is the one true way to ensure we stay in demand and continue delivering value to our customers. Doctors, lawyers, and scientists are all highly respected professionals and all focus on continuously learning. Why should programmers be different?
In this book, we will take a journey that will start with some code that gets the job done and, by selectively applying various DI methods available in Go, together, we will transform it into something a hell of a lot easier to maintain, test, and extend.
Not everything in this book is traditional or perhaps even idiomatic, but I would ask you to try it before you deny it. If you like it, fantastic. If not, at least you learned what you don't want to do.
The saying to a man with only a hammer, every problem looks like a nail is old and yet is never truer than in programming. As professionals, we should be continually striving to acquire more tools to be better equipped for whatever our job throws at us. DI, while a highly useful tool, is useful only for particular nails. In our case, these nails are code smells. Code smells are indications in the code of a potentially deeper problem.
There are many different types of code smell; in this section, we will examine only those that can be alleviated by DI. In later chapters, we will reference these smells as we attempt to remove them from our code.
Code smells generally fall into four different categories:
Code bloat
Resistance to change
Wasted effort
Tight coupling
These are cases where it is difficult and/or slow to add new features. Similarly, tests are often harder to write, especially tests for failure conditions. Similar to code bloat, these smells can be the result of a gradual degradation and lack of maintenance, but they can also be caused by a lack of up-front planning or poor API design.
They can be found by examining the pull request log or commit history and, in particular, determining if new features require many small changes in different parts of the code. If your team tracks feature velocity and you notice it is declining, this is also a likely cause.
These smells include the following:
Shotgun surgery
: This is when small changes made to one struct necessitate changes in other structs. These changes imply that the organisation or abstraction used was incorrect. Typically, all of these changes should be in one class.
In the following example, you can see how adding an email field to the person data would result in changing all three structs (
Presenter
,
Validator
, and
Saver
):
// Renderer will render a person to the supplied writertype Renderer struct{}func (r Renderer) render(name, phone string, output io.Writer) { // output the person}// Validator will validate the supplied person has all the // required fieldstype Validator struct{}func (v Validator) validate(name, phone string) error { // validate the person return nil}// Saver will save the supplied person to the DBtype Saver struct{}func (s *Saver) Save(db *sql.DB, name, phone string) { // save the person to db}
Leaking implementation details
: One of the more popular idioms in the Go community is
accept interfaces, return structs
. It's a catchy turn of phrase, but its simplicity masks its cleverness. When a function accepts a struct, it ties the user to a particular implementation—a strict relationship that makes future changes or additional usage difficult. By extension, if that implementation detail were to change, the API changes and forces changes on its users.
Applying DI to these smells is typically a good investment in the future. While not fixing them is not fatal, the code will progressively degrade until you are dealing with the proverbial big ball of mud. You know the type—a package that no-one understands, no-one trusts, and only the brave or stupid are willing to make changes to. DI enables you to decouple from the implementation choices, thereby making it easier to refactor, test, and maintain small chunks of code in isolation.
These smells are cases where the cost to maintain the code is higher than it needs to be. They are typically caused by laziness or lack of experience. It's always easier to copy/paste code than to carefully refactor it. The problem is, coding like this is like eating unhealthy snacks. It feels great in the moment, but the long-term consequences suck.
They can be found by taking a critical look at the source code and asking yourself do I really need this code? Or, can I make this easier to understand?
Using tools such as dupl (https://github.com/mibk/dupl) or PMD (https://pmd.github.io/) will also help you identify areas of the code to investigate.
These smells include the following:
Excessive duplicated code
: Firstly, please, please do not become a zealot about this one. While in most cases, duplicated code is a bad thing, sometimes copying code can result in a system that is easier to maintain and can evolve. We will deal with a common source of this smell in
Chapter 8
,
Dependency Injection by Config
.
Excessive comments
: Leaving a note for those that come after you, even it is only you 6 months from now, is a friendly and professional thing to do. But when that note becomes an essay, then it's time to refactor:
// Excessive commentsfunc outputOrderedPeopleA(in []*Person) { // This code orders people by name. // In cases where the name is the same, it will order by // phone number. // The sort algorithm used is a bubble sort // WARNING: this sort will change the items of the input array for _, p := range in { // ... sort code removed ... } outputPeople(in)}// Comments replaced with descriptive namesfunc outputOrderedPeopleB(in []*Person) { sortPeople(in) outputPeople(in)}
Overly complicated code
: The harder code is for other people to understand, the worse it is. Typically, this is the result of someone trying to be too fancy or not putting enough effort into structure or naming. Taking a more selfish view, if you are the only one who understands a piece of code, you are the only one that can work on it. Meaning, you are doomed to maintain it forever. What does the following code do:
for a := float64(0); a < 360; a++ { ra := math.Pi * 2 * a / 360 x := r*math.Sin(ra) + v y := r*math.Cos(ra) + v i.Set(int(x), int(y), c)}
DRY/WET code
: The
Don't Repeat Yourself
(
DRY
) principle is aimed at reducing duplicated efforts by grouping responsibilities together and providing clean abstractions. By contrast, in WET code, sometimes called
Waste Everyone's Time
code, you will find the same responsibility in many places. This smell often appears in formatting or conversion code. This sort of code should exist at the system boundaries, that is, converting user input or formatting output.
While many of these smells can be fixed without DI, DI provides an easier way to lift and shift the duplication into an abstraction that can then be used to reduce the duplication and improve the readability and maintainability of the code.
As we journey through this book, you will look at some fantastic coding techniques and some not so great. I would ask you to spend some time pondering which is which. Continuous learning should be tempered with a healthy dose of skepticism. For each technique, I will lay out the pros and cons, but I would ask you to dig deeper. Ask yourself the following:
What is this technique trying to achieve?
What would my code look like after I apply this technique?
Do I really need it?
Are there any downsides to using this method?
Even when your inner skeptic dismisses the technique, you've at least learned to identify something you don't like and don't want to use, and learning is always a win.
Personally, I try to avoid using the term idiomatic Go but a Go book is arguably not complete without addressing it in some form. I avoid it because I have seen it too often used as a stick to beat people. Essentially, this is not idiomatic, therefore it's wrong and, by extension, I am idiomatic and therefore better than you. I believe that programming is a craft and, while a craft should have some form of consistency in its application, it should, as with all crafts, be flexible. After all, innovation is often found by bending or breaking the rules. So what does idiomatic Go mean to me?
I'll define it as loosely as I can:
Format your code with
gofmt
: Truly one less thing for us programmers to argue about. It's the official style, supported with official tools. Let's find something more substantive to argue about.
Read, apply, and regularly revisit the ideas in
Effective Go
(
https://golang.org/doc/effective_go.html
) and
Code Review Comments
(
https://github.com/golang/go/wiki/CodeReviewComments
)
: There is a huge amount of wisdom in these pages, so much so that it's perhaps impossible to glean it all from just one reading.
Aggressively apply the
Unix philosophy
: It state that we should
design code that does a single thing, but to does it well and works well together well with other code
.
While these three things are the minimum for me, there are a couple of other ideas that resonate:
Accepting interfaces and returning structs
: While accepting interfaces leads to nicely decoupled code, the returning structs might strike you as a contradiction. I know they did with me at first. While outputting an interface might feel like it's more loosely coupled, it's not. Output can only be one thing—whatever you code it to be. Returning an interface is fine if that's what you need, but forcing yourself to do so just ends up with you writing more code.
Reasonable defaults
: Since switching to Go, I've found many cases where I want to offer my user the ability to configure the module but such configuration is frequently not used. In other languages, this could lead to multiple constructors or seldom used parameters, but by applying this pattern we end up with a much cleaner API and less code to maintain.
If you were to ask me what is the most frequent mistake new Go programmers make?, I would not hesitate to tell you that it's bringing other language patterns into Go. I know this was my biggest early mistake. My first Go service looked like a Java app written in Go. Not only was the result subpar but it was rather painful, particularly while I was trying to achieve things such as inheritance. I've had a similar experience programming Go in a functional style, as you might see in Node.js.
In short, please don't do it. Re-read Effective Go and Go blogs as often as you need to until you find yourself using small interfaces, firing off Go routines without reservation, loving channels, and wondering why you ever needed more than composition to achieve nice polymorphism.
In this chapter, we started a journey—a journey that will lead to code that is easier to maintain, extend, and test.
We started by defining DI and examining some of the benefits it can bring us. With the help of a few examples, we saw how this might look in Go.
After that, we started identifying code smells to look out for and that could be addressed or alleviated by applying DI.
Finally, we examined what I believe Go code looks like, and I challenged you to be skeptical and apply a critical eye to techniques presented in this book.
What is DI?
What are the four highlighted advantages of DI?
What sorts of issues does it address?
Why is it important to be skeptical?
What does idiomatic Go mean to you?
Packt has many other great resources for learning about DI and Go:
https://www.packtpub.com/application-development/java-9-dependency-injection
https://www.packtpub.com/application-development/dependency-injection-net-core-20
https://www.packtpub.com/networking-and-servers/mastering-go
In 2002, Robert "Uncle Bob" Martin published the book Agile Software Development, Principles, Patterns, and Practices in which he defined the five principles of reusable programs, which he called SOLID principles. While it might seem strange to include these principles in a book about a programming language invented 10 years later, these principles are still relevant today.
In this chapter, we will briefly examine each of these principles, how they relate to dependency injection (DI) and what that means for Go. SOLID is an acronym for five popular object-oriented software design principles:
Single responsibility principle
Open/closed principle
Liskov substitution principle
Interface segregation principle
Dependency inversion principle
The only requirement for this chapter is a basic understanding of objects and interfaces and an open mind.
All code in this chapter is available at https://github.com/PacktPublishing/Hands-On-Dependency-Injection-in-Go/tree/master/ch02.
You will find links to additional information and other references mentioned in this chapter in the Further reading section at the end of this chapter.
Go doesn't have classes, but if we squint a little and replace the word class with objects (structs, functions, interfaces or packages), then the concept still applies.
Why do we want our objects to do only one thing? Let's look at a couple of objects that do one thing:
These objects are simple and easy to use, and have a wide range of uses.
Designing objects so that they all do only one thing sounds okay in the abstract. But you are probably thinking that doing so for an entire system would add a lot more code. Yes, it will. However, what it doesn't do is add complexity; in fact, it significantly reduces it. Each piece of code would be smaller and easier to understand, and therefore easier to test. This fact gives us the first advantage of SRP:
SRP reduces the complexity by decomposing code into smaller, more concise pieces
With a name like single responsibility principle, it would be safe to assume that it is all about responsibility, but so far, all we have talked about is change. Why is this? Let's look at an example:
// Calculator calculates the test coverage for a directory // and it's sub-directoriestype Calculator struct { // coverage data populated by `Calculate()` method data map[string]float64}// Calculate will calculate the coverage
