[Go]Bank 프로젝트
private struct 생성
구조체(struct, 자바에서의 클래스)를 만들 때 export 하기 위해서는 구조체 이름과 내부 변수명을 대문자로 적어 public하게 만들어야 한다. 하지만 여기서 구조체를 private 하게 만들기 위해서는 다르 언어에서 처럼 생성자를 만들어줘야 하는데 Golang에는 생성자를 만들 수 없다. 그래서 Golang에서는 흔히 function으로 private한 구조체를 생성한다.
public 구조체
구조체 이름과 내부 변수를 대문자로 적어 public 구조체를 생성하여 다른 파일로 export/import 할 수 있게 만들 수 있다.
package main
import (
"github.com/saichoi/learngo/accounts"
)
func main() {
account := accounts.Account{Owner: "choi"}
account.Owner = "dahye"
}
package accounts
// Account struct
type Account struct {
Owner string
balance int
}
private 구조체
생성자가 없는 Golang에서는 func을 사용하여 private 구조체를 만들 수 있다.
package main
import (
"fmt"
"github.com/saichoi/learngo/accounts"
)
func main() {
// accounts.go 파일에 생성해둔 private한 struct를 사용
account := accounts.NewAccount("choi")
fmt.Println(account)
}
package accounts
// Account struct
type Account struct {
owner string
balance int
}
// NewAccount creates Account
// *, & : pointer를 사용하면 메모리를 적게 차지 하기 때문에 실행 속도가 빨라진다.
func NewAccount(owner string) *Account { // *Account : 위에 선언한 Account를 그대로 사용하기 위함
account := Account{owner: owner, balance: 0}
return &account // &account : 새로 생성하지 않고 복사본을 그대로 return한다.
}
터미널 실행
터미널에서 파일을 실행하면 &가 붙어 있는 것을 확인할 수 있다. 새로 생성하지 않고 기존에 존재하던 데이터를 그대로 반환했다는 것을 알 수 있다.
private 구조체 값 변경
private 구조체는 일반적인 방법으로는 값을 변경할 수 없다. 이를 변경하기 위해서 method가 필요하다. Golang에서의 method는 구조체 내부에 선언하는 것이 아니라 func에 reciver을 추가하는 것으로 생성할 수 있다. receiver의 이름은 구조체의 앞글자(소문자)를 따온다.
method 생성
// method 형식
func (리시버명 리시버타입) 메소드명(인자명 인자타입) {
}
package main
import (
"fmt"
"github.com/saichoi/learngo/accounts"
)
func main() {
account := accounts.NewAccount("choi")
account.Deposit(10)
fmt.Println(account.Balance())
}
package accounts
import "fmt"
// Account struct
type Account struct {
owner string
balance int
}
// NewAccount creates Account
// *, & : pointer를 사용하면 메모리를 적게 차지 하기 때문에 실행 속도가 빨라진다.
func NewAccount(owner string) *Account { // *Account : 위에 선언한 Account를 그대로 사용하기 위함
account := Account{owner: owner, balance: 0}
return &account // &account : 새로 생성하지 않고 기존에 존재하던 데이터를 그대로 return한다.
}
// Deposit x amount on your account
func (a Account) Deposit(amount int) {
fmt.Println("Gonna deposit", amount)
a.balance += amount
}
// Balance of your account
func (a Account) Balance() int {
return a.balance
}
// Deposit x amount on your account
func (a *Account) Deposit(amount int) {
fmt.Println("Gonna deposit", amount)
a.balance += amount
}
// Balance of your account
func (a Account) Balance() int {
return a.balance
}
Deposit(예금) 메소드에서 사용하여는 Account는 기존의 데이터를 활용하여 연산해야 하기 때문에 pointer(*)를 붙여 Go에게 복사본을 만들지 않도록 알려준다.
method 예외처리
위와 같은 방법으로 Withdraw(인출) 메소드를 만들 수 있다.
// Withdraw x amount from your account
func (a *Account) Withdraw(amount int) {
a.balance -= amount
}
하지만 인출의 경우 잔액이 부족한 경우 실행이 되서는 안된다.
package main
import (
"fmt"
"github.com/saichoi/learngo/accounts"
)
func main() {
// accounts.go 파일에 생성해둔 private한 struct를 사용
account := accounts.NewAccount("choi")
account.Deposit(10)
fmt.Println(account.Balance())
account.Withdraw(20)
fmt.Println(account.Balance())
}
이러한 문제를 해결하기 위해서는 예외처리를 해줘야하는데 Golang에서는 try ~ catch와 같은 문법이 존재하지 않기 때문에 직접 에러 처리를 해줘야 한다.
// Withdraw x amount from your account (인출)
func (a *Account) Withdraw(amount int) error {
if a.balance < amount {
return errors.New("Can't withdraw you are poor")
}
a.balance -= amount
return nil
}
func main() {
account := accounts.NewAccount("choi")
account.Deposit(10)
fmt.Println(account.Balance())
err := account.Withdraw(20)
if err != nil {
fmt.Println(err)
}
fmt.Println(account.Balance())
}
코드의 재활용을 위해서 에러를 변수로 선언해주는 것이 더 효율적이다.
var erroNoMoney = errors.New("Can't withdraw")
...
// Withdraw x amount from your account (인출)
func (a *Account) Withdraw(amount int) error {
if a.balance < amount {
return erroNoMoney
}
a.balance -= amount
return nil
}
method 내부적 호출
Go의 구조체는 내부적으로 자동 method를 호출 기능을 가지고 있다.
// ChangeOwner of the account
func (a *Account) ChangeOwner(newOwner string) {
a.owner = newOwner
}
// Owner of the account
func (a Account) Owner() string {
return a.owner
}
func (a Account) String() string {
return fmt.Sprint(a.Owner(), "'s account.\nHas: ", a.Balance())
}
package main
import (
"fmt"
"github.com/saichoi/learngo/accounts"
)
func main() {
account := accounts.NewAccount("choi")
account.Deposit(10)
fmt.Println(account)
}