This Guide is builds upon the previous four articles over at https://codingwithdrew.com/category/golang/ - if you haven't already, I recommend you start there and work your way here.
Overview:
We will create a package that will simulate playing cards you'd use for poker or the like.
We are going to create a number of functions that will simulate how we interact with the deck of cards.
newDeck
-- create a list of playing cards. Essentially an array of strings where each element of an array will represent a single face of card.We haven't discussed an array just yet, but keep that question in your pocket, we will get there ๐
deal
-- create a 'hand' of cards.shuffle
-- shuffles all the cards in the decknewDeckFromFile
-- load a list of cards from the local machine.saveToFile
-- save a list of cards to a file on the local machineprint
-- log out of the contents of a deck of cards
How to organize the project
Our files will be structured like this:
~/Dev/Cards/ Directory
|-- main.go (Code to create and manipulate a deck)
|-- deck.go (code that will describe what a deck is and how it works)
|-- deck_test.go (code that automatically test the deck)
Object Oriented Programming and Go
In languages like Javascript you can create object blueprints called classes. This makes creating new objects easier.
If this were Javascript, we would declare a class and provide it some basic functionality and structure. It'll have some properties and methods to describe what a copy will look like. We can then use that class to create an instance which will be more specific than the class.
Ok, ok, I know what I just wrote is really confusing but let me suggest a car analogy.
Imagine that we can create a blueprint/idea that describes how to describe cars. Every car will have tires, a chasis, an engine, a manufacture, a color, a year and a model.
We can create a class and call it "cars" to describe what kind of data would be used to describe each property I just mentioned. Now if we created a new "car" from the class "cars" we could provide it some details like the year it was made and that new "car" we created would be the equivalent of the "instance" described above.
We could even take the idea/blueprint of the class "cars" and add to it or "extend" it by adding whether it was an electric car or manufactured in the US and you wouldn't have to redeclare all the other properties, just the ones that changed.
In go, there is no such thing as classes.
Data Types in Go:
- string
- integer
- float
- array
- map
How does go approach this kind of programing paradigm?
If we wanted to "extend" the base data types in go and add some functionality to it, we'd tell Go we want to create an array of strings and then add a bunch of functions speifically made to work with it.
This idea is called types
and it would be implemented is type deck []string
for example.
Functions with "deck" as a "reciver" -- A "function with a reciever" is like a "method" or a function that belongs to an "instance"
This is a common pattern in Go.
Let's create deck.go & deck_test.go
- Make sure you are in the project directory. Run
touch deck.go
&deck_test.go
. - We know that these two files will be a part of the same project so we know they will have at least 1 line:
package main
at the start of the file.
Declare a custom Deck Type
๐ก When we declare a new type, we can relate it to Object oriented programing and say the
type
we are creating "extends" the "data type" we are declaring. Example: Intype deck []string
we are saying loosely that "deck extends string" even though GoLang doesn't officially support that nominclature. More straight forward, we are saying "deck has all the same behavior as string".
In deck.go:
package main
// CREATE A NEW TYPE OF 'DECK'
// WHICH IS A SLICE OF STRINGS
type deck []string
By creating a new deck type that acts as a string, we can replace []string
from our main.go file with deck
package main
import "fmt"
func main() {
cards := deck{"Ace of Clubs", myCard()}
cards = append(cards, "King of Hearts")
for i, card := range cards {
fmt.Println(i, card)
}
}
func myCard() string {
return "Ace of Diamonds"
}
To prove that this code still works, we can run it to see what happens.
In terminal, run: go run main.go deck.go
, you should see it's output being the same as before.
๐ก Remember: "
go run
- Complies and executes one or two files, note that it's similar to build but also executes."
That's cool, but what's the benefit? Seems like the original code with extra steps.
In our deck.go file we want to update it to look like this:
package main
import "fmt"
// CREATE A NEW TYPE OF 'DECK'
// WHICH IS A SLICE OF STRINGS
type deck []string
func (props deck) print() {
for i, card := range props {
fmt.Println(i, card)
}
}
Let's break down what we just did but first let's take advantage of that by updating our main.go file.
package main
import "fmt"
func main() {
cards := deck{"Ace of Clubs", myCard()}
cards = append(cards, "King of Hearts")
cards.print()
}
func myCard() string {
return "Ace of Diamonds"
}
๐จ Don't forget! Press
command
+option
+S
to save your working directory and all files that changed thengit add .
followed bygit commit -m "<INSERT SOME COMMENT HERE>"
,git push
, and then enter your password.
The Reciever function and how this code works
func (props deck) print() {}
๐ก In the line above, we are creating a function with a reciever where the reciever is
deck
and we are creating our own version of it called "props" locally inside this function.๐ก By convention in go, typically we would use a single letter in place of the
props
variable we pass and it would typically be the first letter of the type we are accessing so for example in this case, we would use:func (d deck) print(){}
but I'm going to leave it alone for this example. You can do anything you want including calling itthis
.๐ก What we are saying is "Any variable of type
deck
and cards is of typedeck
it now gets access to theprint()
method.
We then pass props
into our function like so:
for i, card := range props {
fmt.Println(i, card)
Basically, what happens here is that when deck is called in main()
we are creating a new splice of strings from deck
called cards
and then passing that deck from main.go to deck.go using props
(the reciever). Now we can call our print()
method on our cards
variable like so:
cards.print()
Drew is a seasoned DevOps Engineer with a rich background that spans multiple industries and technologies. With foundational training as a Nuclear Engineer in the US Navy, Drew brings a meticulous approach to operational efficiency and reliability. His expertise lies in cloud migration strategies, CI/CD automation, and Kubernetes orchestration. Known for a keen focus on facts and correctness, Drew is proficient in a range of programming languages including Bash and JavaScript. His diverse experiences, from serving in the military to working in the corporate world, have equipped him with a comprehensive worldview and a knack for creative problem-solving. Drew advocates for streamlined, fact-based approaches in both code and business, making him a reliable authority in the tech industry.