Lesson 3
Go Encapsulation and Access Control: Mastering Privacy with Naming Conventions
Lesson Overview

Hello! In this lesson, we're diving into Encapsulation and access control in Go. Unlike some languages, Go manages encapsulation through capitalization conventions. Imagine encapsulation as an invisible barrier that protects the internals of your code. In Go, identifiers (like struct fields and methods) are kept safe using naming conventions: uppercase indicates exported (public), while lowercase indicates unexported (private). This is crucial for creating robust and secure applications!

Into the Encapsulation

In Go, encapsulation is achieved through structs and methods. Structs bundle together data, while methods attach functionality. Please note that in Go, the distinction between exported (public) and unexported (private) identifiers in Go is meaningful only across package boundaries, so all code snippets we'll discuss today will span multiple files. As example, imagine to be coding a multiplayer game; you could create a Player struct, encapsulating fields (health, armor, stamina) and methods (ReceiveDamage, ShieldHit, RestoreHealth).

File content for player/player.go:

Go
1package player 2 3type Player struct { 4 health int 5 armor int 6 stamina int 7} 8 9// Constructor function for creating new Player 10func NewPlayer(health, armor, stamina int) *Player { 11 return &Player{health: health, armor: armor, stamina: stamina} 12} 13 14// Public method to receive damage 15func (p *Player) ReceiveDamage(damage int) { 16 p.health -= damage // Reduce health 17} 18 19// Public method to decrease armor 20func (p *Player) ShieldHit(armorDecrease int) { 21 p.armor -= armorDecrease // Decrease armor 22} 23 24// Public method to restore health 25func (p *Player) RestoreHealth(healthIncrease int) { 26 p.health += healthIncrease // Restore health 27}

File content for main.go:

Go
1package main 2 3import ( 4 "fmt" 5 "player" 6) 7 8func main() { 9 player := player.NewPlayer(100, 50, 77) 10 player.ReceiveDamage(10) 11 player.ShieldHit(5) 12 player.RestoreHealth(15) 13 fmt.Println("Player's Current Health:", player) // Output: Player's Current Health: &{105 45 77} 14}

Here, player is an instance of the Player struct with methods you can call to manipulate its state.

Remark the Privacy

In Go, access control is managed through capitalization. An identifier, such as a field or method, is exported (similar to "public") if it begins with an uppercase letter. Conversely, if it starts with a lowercase letter, it remains unexported (similar to "private"). Note that this access control is enforced only across package boundaries.

File content for privacy/privacy.go:

Go
1package privacy 2 3type PrivateExample struct { 4 PublicAttribute string 5 privateAttribute string 6} 7 8// Constructor-like function for creating a new PrivateExample with privateAttribute 9func NewPrivateExample(publicAttr, privateAttr string) *PrivateExample { 10 return &PrivateExample{PublicAttribute: publicAttr, privateAttribute: privateAttr} 11} 12 13// Method that prints attributes (for demonstration purposes) 14func (pe *PrivateExample) PrintAttributes() { 15 fmt.Println("Public Attribute:", pe.PublicAttribute) 16 fmt.Println("Private Attribute:", pe.privateAttribute) 17}

File content for main.go:

Go
1package main 2 3import ( 4 "fmt" 5 "example" 6) 7 8func main() { 9 example := privacy.NewPrivateExample("Public", "Private") 10 example.PrintAttributes() 11 fmt.Println(example.PublicAttribute) // Output: Public 12 // Uncommenting the following line would cause an error because privateAttribute is unexported 13 // fmt.Println(example.privateAttribute) 14}
Private Attributes

Private-like access to struct fields in Go is controlled by making struct fields unexported, limiting outside interference and ensuring data integrity. Let's consider a BankAccount use case and let's see how we can implement this.

File content for bankaccount/bankaccount.go:

Go
1package bankaccount 2 3type BankAccount struct { 4 accountNumber int 5 balance float64 6} 7 8// Constructor function for creating new BankAccount 9func NewBankAccount(accountNumber int, balance float64) *BankAccount { 10 return &BankAccount{accountNumber: accountNumber, balance: balance} 11} 12 13// Public method to deposit money 14func (ba *BankAccount) Deposit(amount float64) { 15 ba.balance += amount // Deposit money 16} 17 18// Method to print balance (for demonstration purposes) 19func (ba *BankAccount) PrintBalance() { 20 fmt.Println("Balance:", ba.balance) 21}

File content for main.go:

Go
1package main 2 3import ( 4 "bankaccount" 5) 6 7func main() { 8 bankAccount := bankaccount.NewBankAccount(1234, 100.0) 9 bankAccount.PrintBalance() 10 // Uncommenting the following line would cause an error because balance is unexported 11 // fmt.Println(bankAccount.balance) 12}

Here, balance is kept unexported, safeguarding the account's integrity.

Private Methods

Private-like methods in Go are handled through naming conventions. Methods beginning with a lowercase letter are unexported and can't be accessed outside their package.

File content for bankaccount/bankaccount.go:

Go
1package bankaccount 2 3type BankAccount struct { 4 accountNumber int 5 balance float64 6} 7 8// Constructor function for creating new BankAccount 9func NewBankAccount(accountNumber int, balance float64) *BankAccount { 10 return &BankAccount{accountNumber: accountNumber, balance: balance} 11} 12 13// Public method that calls the unexported addInterest method 14func (ba *BankAccount) AddYearlyInterest() { 15 ba.addInterest(0.02) // Adds 2% interest 16} 17 18// Unexported method 19func (ba *BankAccount) addInterest(interestRate float64) { 20 ba.balance += ba.balance * interestRate // Calculation of interest 21} 22 23// Method to print balance (for demonstration purposes) 24func (ba *BankAccount) PrintBalance() { 25 fmt.Println("Balance:", ba.balance) 26}

File content for main.go:

Go
1package main 2 3import ( 4 "bankaccount" 5) 6 7func main() { 8 bankAccount := bankaccount.NewBankAccount(1234, 100.0) 9 bankAccount.AddYearlyInterest() 10 bankAccount.PrintBalance() 11 // Uncommenting the following line would cause an error because addInterest is unexported 12 // bankAccount.addInterest(0.1) 13}

Here, AddYearlyInterest is an exported method that calls the unexported method addInterest.

Lesson Summary and Practice

Fantastic work exploring Go's approach to encapsulation through capitalization conventions of struct fields and methods! By understanding and applying these key principles across package boundaries, your Go code becomes robust, secure, and maintainable.

Next, let’s put these concepts into practice. Get ready for some hands-on exercises to solidify what you've learned! Keep coding — exciting challenges await!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.