解答 01.1 — 構造体とメソッド(MonthlyFee)¶
このノートブックは 01.1-structs-and-methods.ipynb の練習問題の解答です。
まず問題の前提コードを再掲し、次に解答、最後にチェックを実行します。
先に自分の力で解いてから、答え合わせに使ってください。
前提コード(問題ノートブックと同じ定義)¶
In [1]:
import (
"errors"
"fmt"
"github.com/janpfeifer/gonb/gonbui"
)
In [2]:
type BankAccount struct {
owner string
balance int
}
func NewBankAccount(owner string, initial int) *BankAccount {
return &BankAccount{owner: owner, balance: initial}
}
func (a *BankAccount) Deposit(amount int) error {
if amount <= 0 {
return fmt.Errorf("deposit amount must be positive: %d", amount)
}
a.balance += amount
return nil
}
func (a *BankAccount) Withdraw(amount int) error {
if amount <= 0 {
return fmt.Errorf("withdraw amount must be positive: %d", amount)
}
if amount > a.balance {
return fmt.Errorf("insufficient funds: balance=%d, withdraw=%d", a.balance, amount)
}
a.balance -= amount
return nil
}
func (a *BankAccount) Balance() int {
return a.balance
}
解答: MonthlyFee¶
Withdraw と同様に、「不正な入力はエラー」「残高不足はエラー(残高を変えない)」
「成功したら残高を引く」の順で書きます。
In [3]:
func (a *BankAccount) MonthlyFee(fee int) error {
if fee <= 0 {
return fmt.Errorf("monthly fee must be positive: %d", fee)
}
if fee > a.balance {
return fmt.Errorf("insufficient funds for monthly fee: balance=%d, fee=%d", a.balance, fee)
}
a.balance -= fee
return nil
}
チェック(問題ノートブックと同じ期待値)¶
答え合わせ用のヘルパー mustEqual を定義します。
In [4]:
import "reflect"
import "fmt"
func mustEqual(got, want any, name string) {
if reflect.DeepEqual(got, want) {
fmt.Printf("✅ Passed: %s\n", name)
return
}
panic(fmt.Sprintf("❌ %s\n got = %v (%T)\n want = %v (%T)", name, got, got, want, want))
}
In [5]:
%%
acc := NewBankAccount("hanako", 1000)
err1 := acc.MonthlyFee(100)
mustEqual(err1, nil, "MonthlyFee(100) はエラーなし")
mustEqual(acc.Balance(), 900, "残高が 1000 → 900")
err2 := acc.MonthlyFee(5000)
mustEqual(err2 != nil, true, "残高不足(5000)はエラー")
mustEqual(acc.Balance(), 900, "エラー時は残高が変わらない")
err3 := acc.MonthlyFee(-1)
mustEqual(err3 != nil, true, "負の手数料はエラー")
println("🎉 すべてのチェックが通りました")
✅ Passed: MonthlyFee(100) はエラーなし ✅ Passed: 残高が 1000 → 900 ✅ Passed: 残高不足(5000)はエラー ✅ Passed: エラー時は残高が変わらない ✅ Passed: 負の手数料はエラー
🎉 すべてのチェックが通りました
解説¶
fee <= 0のガード: 手数料が 0 以下という不正な入力を、最初に弾いています(エラーを返す)- 残高不足のガード:
fee > a.balanceならエラーを返します。この時点でまだa.balanceを引いていないことが大切です。「残高を変えない」はエラー処理の鉄則です - 成功時: 最後にだけ
a.balance -= feeを実行します
この「不正は先に弾く → 成功操作は最後に」という順序は、Withdraw と同じ構造です。
状態を変更するメソッドは常にこの順序で書く習慣をつけましょう。