# Encapsulation

Course: [Go](https://www.devtutorials.ai/learn/go) · Section: Types and OOP
Source: https://www.devtutorials.ai/learn/go/types-and-oop/encapsulation

> Hide data in Go with package-level visibility: export rules, getter methods for read access, read-only patterns, and setter methods with validation.

## What Is Encapsulation?

**Encapsulation** = keeping a type's data hidden, and letting other code change it **only** through the type's own methods.

- The data stays safe inside.
- Outside code uses methods like `Deposit()` or `SetMarks()`.
- The methods check every change before making it.

**Simple lesson:** encapsulation hides the data and lets in only safe changes.

### A Real-World Example

Think of the school's exam marks register.

- Teachers cannot write in it directly.
- They fill a **form** and give it to the exam office.
- The office **checks** the form: marks between 0 and 100? Correct roll number?
- Only then is the register updated.
- Anyone can **ask** for a mark, but only through the office.

Now the mapping:

- **The register** = the hidden fields.
- **The exam office** = the type's methods.
- **Checking the form** = validation inside a setter.
- **Asking for a mark** = a getter.

Go has no `private` or `public` keywords.

It uses the **first letter** of a name. You met this in **Packages and Modules**:

- **Capital** = exported: other packages can use it.
- **small** = unexported: only its own package can use it.

## What You Will Learn

In this lesson, you will learn:

1. What encapsulation and data hiding are.
2. How package-level visibility works in Go.
3. The export rules: capitalized and lowercase names.
4. How to give read access with getter methods.
5. How to control changes with setter methods and validation.

## Encapsulation Fundamentals

### Data Hiding

**Data hiding** = making a field unreachable from outside, so nobody can set a wrong value.

Without hiding:

- Anyone can write `account.Balance = -5000`.
- Nothing stops a negative balance.
- The bug shows up much later, far from where it happened.

With hiding:

- The field is `balance` (lowercase).
- The only way in is `Deposit(amount)`, which refuses a negative amount.

### Package-Level Visibility

Go hides things at the **package** level, not the type level.

- Code **inside** the same package can see lowercase names.
- Code in **another** package cannot.
- So to hide a field, put the type in its **own package**.

```text
school/
├── go.mod
├── main.go            package main
└── bank/
    └── account.go     package bank
```

```go
// bank/account.go
package bank

import "errors"

type Account struct {
	owner   string // unexported: hidden from other packages
	balance int    // unexported
}

func New(owner string) *Account {
	return &Account{owner: owner}
}

func (a *Account) Deposit(amount int) error {
	if amount <= 0 {
		return errors.New("deposit must be positive")
	}
	a.balance += amount
	return nil
}

func (a *Account) Balance() int {
	return a.balance
}
```

```go
// main.go
package main

import (
	"fmt"

	"example.com/school/bank"
)

func main() {
	acc := bank.New("Asha")
	acc.Deposit(500)
	fmt.Println(acc.Balance()) // 500
	// acc.balance = -100      // error: acc.balance undefined (cannot refer to unexported field balance)
}
```

- `main` can call `New`, `Deposit` and `Balance`, because they are capitalized.
- `main` cannot touch `balance`. Go refuses to build.

In this lab, each program is one file in `package main`.

Inside one package, Go lets every function see lowercase fields. So the runnable examples below show the **pattern** (methods guarding the data), but they cannot show Go blocking outside access. In a real project, put the type in its own package.

**Simple lesson:** to hide data, make fields lowercase and put the type in its own package.

## Export Rules

### Capitalized Names

**Exported** = starts with a capital letter. Visible to every package that imports this one.

- types: `Account`, `Student`
- functions: `New`, `Average`
- methods: `Deposit`, `Balance`
- fields: `Name`, `Marks`
- constants and variables: `MaxMarks`

### Lowercase Names

**Unexported** = starts with a small letter (or `_`). Visible only inside its own package.

- helper functions: `total`, `validate`
- hidden fields: `balance`, `owner`
- internal constants: `maxRetries`

### Mixed Structs

A struct can have both kinds of fields.

```go
type Student struct {
	Name  string // exported: anyone can read and set it
	marks int    // unexported: only through methods
}
```

- Use exported fields for plain data with no rules.
- Use unexported fields for data that must stay valid.

```go runnable title="Some fields open, some guarded"
package main

import "fmt"

type Student struct {
	Name  string
	marks int
}

func (s *Student) AddMarks(n int) {
	if n > 0 {
		s.marks += n
	}
}

func (s Student) Marks() int {
	return s.marks
}

func main() {
	s := Student{Name: "Ravi"}
	s.Name = "Ravi Kumar"
	s.AddMarks(40)
	s.AddMarks(-100)
	fmt.Println(s.Name, s.Marks())
}
```

```text
Ravi Kumar 40
```

- `Name` is set directly: it has no rules.
- `marks` changes only through `AddMarks`, which ignores negative numbers.

**Simple lesson:** a capital first letter shares a name, and a small one keeps it inside the package.

## Getter Methods

### Read Access

**Getter** = a method that returns the value of a hidden field.

- In Go, a getter is named after the field, **without** "Get": `Balance()`, not `GetBalance()`.
- It usually has a value receiver, or a pointer receiver if the type uses pointers.

```go runnable title="A getter"
package main

import "fmt"

type ReportCard struct {
	student string
	marks   int
}

func (r ReportCard) Student() string {
	return r.student
}

func (r ReportCard) Marks() int {
	return r.marks
}

func main() {
	rc := ReportCard{student: "Meena", marks: 88}
	fmt.Println(rc.Student(), rc.Marks())
}
```

```text
Meena 88
```

### Read-Only Access Patterns

**Read-only** = outside code can see a value but never change it.

- Give a **getter**, and **no setter**.
- Return **copies** of slices, so the caller cannot change your hidden slice.

```go runnable title="Return a copy, keep the original safe"
package main

import "fmt"

type Class struct {
	names []string
}

func (c *Class) Add(name string) {
	c.names = append(c.names, name)
}

func (c *Class) Names() []string {
	out := make([]string, len(c.names))
	copy(out, c.names)
	return out
}

func main() {
	c := &Class{}
	c.Add("Asha")
	c.Add("Ravi")
	list := c.Names()
	list[0] = "HACKED"
	fmt.Println(c.Names())
}
```

```text
[Asha Ravi]
```

- `Names()` gives a **copy**. Changing `list` does not change the class.
- Returning `c.names` directly would let callers change it. See **Slices**.

**Simple lesson:** a getter shares a value, and returning a copy keeps it read-only.

## Setter Methods

### Controlled Updates

**Setter** = a method that changes a hidden field, after checking the new value.

- Name it `SetX`: `SetMarks`, `SetPhone`.
- It needs a **pointer receiver**, because it changes the struct. See **Structs and Methods**.
- It usually returns an `error` when the value is wrong.

### Validation Logic

**Validation** = checking that a value follows the rules before using it.

Press **Play** to watch a setter protect the marks.

```go runnable title="A bank account with rules"
package main

import (
	"errors"
	"fmt"
)

type Account struct {
	owner   string
	balance int
}

func NewAccount(owner string) *Account {
	return &Account{owner: owner}
}

func (a *Account) Deposit(n int) error {
	if n <= 0 {
		return errors.New("deposit must be positive")
	}
	a.balance += n
	return nil
}

func (a *Account) Withdraw(n int) error {
	if n > a.balance {
		return errors.New("not enough balance")
	}
	a.balance -= n
	return nil
}

func (a *Account) Balance() int {
	return a.balance
}

func main() {
	acc := NewAccount("Kabir")
	fmt.Println(acc.Deposit(500))
	fmt.Println(acc.Withdraw(800))
	fmt.Println(acc.Withdraw(200))
	fmt.Println("Balance:", acc.Balance())
}
```

```text
<nil>
not enough balance
<nil>
Balance: 300
```

- `NewAccount` is the only way to make an account with an owner.
- `Deposit` and `Withdraw` refuse bad changes.
- `Balance` lets anyone read, but not write.

A second example: a phone number must have exactly 10 digits.

```go runnable title="Validate a phone number"
package main

import (
	"errors"
	"fmt"
)

type Contact struct {
	phone string
}

func (c *Contact) SetPhone(p string) error {
	if len(p) != 10 {
		return errors.New("phone must have 10 digits")
	}
	for _, r := range p {
		if r < '0' || r > '9' {
			return errors.New("phone must have only digits")
		}
	}
	c.phone = p
	return nil
}

func (c Contact) Phone() string {
	return c.phone
}

func main() {
	var c Contact
	fmt.Println(c.SetPhone("98450"))
	fmt.Println(c.SetPhone("98450abcde"))
	fmt.Println(c.SetPhone("9845012345"), c.Phone())
}
```

```text
phone must have 10 digits
phone must have only digits
<nil> 9845012345
```

- Two rules, checked in order. The first broken rule is reported.
- Only a valid number is stored.

**Easy way to remember**

- Hide the field: lowercase.
- Let people read: a getter named after the field.
- Let people change it safely: a `SetX` method that checks and returns an error.
- Keep the type in its own package.

**Simple lesson:** a setter checks a value and refuses it with an error, so the hidden data always stays valid.

## TL;DR

1. **Encapsulation** = hide data, change it only through methods.
2. **Data hiding** = lowercase fields. Go hides them from **other packages**.
3. **Exported** = Capital first letter. **Unexported** = small first letter.
4. **Getter** = `Balance()`, named after the field, no "Get".
   - **Read-only** = a getter with no setter. Return copies of slices.
5. **Setter** = `SetMarks(m) error`, pointer receiver, checks first.
   - **Validation** = refuse bad values with an error.
6. Put the type in its own package so the hiding really works.

## Common mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| Using `private` or `public` | Go has these keywords | Go uses the first letter | A syntax error. |
| Lowercase fields in `package main` and expecting them hidden | Hiding is per type | Hiding is per package | Code in the same package can still change them. |
| `GetBalance()` | Getters start with Get | Go style is `Balance()` | Works, but looks out of place. |
| A setter with a value receiver | Any receiver changes the struct | Setters need `*T` | The change is lost. |
| A setter that never checks | Setters only set | Setters guard the rules | Bad data gets in. |
| Returning a hidden slice directly | Callers only read it | They can change its elements | Hidden data changes from outside. |
| Lowercase type name with an exported constructor | Fine everywhere | Callers cannot even name the type | Awkward to use. |

## Check Yourself

## Exercises

## What to Remember

- Encapsulation hides data and lets it change only through methods.
- Go uses the first letter: Capital is exported, small is unexported.
- Hiding works between packages, so put the type in its own package.
- Getters are named after the field: `Balance()`.
- Setters use a pointer receiver, check the value, and return an error.
- Return copies of hidden slices to keep them read-only.

Next: **Advanced Type Design**. You will learn custom types, type aliases, method sets and interface composition.
