回顧 Day 2 我們做的 Gross Store 練習 今天發生突發事件!!!!
Gross 雜貨舖最近生意太好,顧客一多,問題也跟著來啦!
今天小老闆要帶大家看看:如果顧客喊錯單位、退太多東西、或是查詢根本不存在的商品,**要怎麼用程式「防呆」和「錯誤處理」呢?  **
顧客說:「老闆,給我一個 mega_gross 的柳丁!」
但我們的 Units 根本沒有 mega_gross。
👉 請你修改 AddItem,讓它能回傳 error,避免錯誤單位被加入。
AddItem 回傳 bool,只能表示成功/失敗,資訊不足。error,將錯誤原因傳遞出去。func AddItem(bill, units map[string]int, item, unit string) error {
    qty, ok := units[unit]
    if !ok {
        return fmt.Errorf("不存在的單位: %s", unit)
    }
    bill[item] += qty
    return nil
}
測試:
err := AddItem(bill, units, "orange", "mega_gross")
if err != nil {
    fmt.Println(err) // ❌ 不存在的單位: mega_gross
}
👉 答案: 呼叫時會輸出錯誤,而不會亂塞進帳單。
顧客說:「我要退 2 打香蕉啦!」
結果,實際上他只買了半打。
👉 請你修改 RemoveItem,讓它能檢查數量不足並回傳錯誤訊息。
func RemoveItem(bill, units map[string]int, item, unit string) error {
    qty, ok := units[unit]
    if !ok {
        return fmt.Errorf("不存在的單位: %s", unit)
    }
    current, exists := bill[item]
    if !exists {
        return fmt.Errorf("帳單上沒有 %s", item)
    }
    if current < qty {
        return fmt.Errorf("退貨數量太多 (現有 %d, 退 %d)", current, qty)
    }
    if current == qty {
        delete(bill, item)
    } else {
        bill[item] -= qty
    }
    return nil
}
測試:
err := RemoveItem(bill, units, "banana", "dozen")
if err != nil {
    fmt.Println(err) 
    // ❌ 退貨數量太多 (現有 6, 退 12)
}
👉 答案: 傳回錯誤提示,避免出現「負數存貨」。
顧客問:「老闆,我帳單裡有葡萄嗎?」
但實際上他從來沒買過葡萄。
👉 請你修改 GetItem,讓它在查不到商品時回報錯誤。
func GetItem(bill map[string]int, item string) (int, error) {
    qty, ok := bill[item]
    if !ok {
        return 0, fmt.Errorf("帳單上沒有 %s", item)
    }
    return qty, nil
}
測試:
qty, err := GetItem(bill, "grape")
if err != nil {
    fmt.Println(err) // ❌ 帳單上沒有 grape
}
👉 答案: 程式輸出錯誤,避免誤以為有這個商品。
錯誤訊息太多時,老闆決定寫一個小工具,統一印出 log。
import "log"
func CheckErr(err error) {
    if err != nil {
        log.Println("❌", err)
    }
}
測試:
err := AddItem(bill, units, "pear", "super_gross")
CheckErr(err)
// log: ❌ 不存在的單位: super_gross
👉 答案: 錯誤訊息整齊一致,未來也方便擴充(例如記錄寫進檔案、丟到監控系統)。
為什麼要錯誤處理?
設計要點
bool 替換成 error,訊息更清楚。CheckErr 幫手,讓程式看起來更乾淨。小老闆的心得
「以前帳單常常亂掉,現在加了錯誤處理,顧客怎麼刁難我都能穩穩守住!」 🎉
package main
import (
    "fmt"
    "log"
)
// -----------------------------
// 基礎函式
// -----------------------------
func Units() map[string]int {
    return map[string]int{
        "quarter_of_a_dozen": 3,
        "half_of_a_dozen":    6,
        "dozen":              12,
        "small_gross":        120,
        "gross":              144,
        "great_gross":        1728,
    }
}
func NewBill() map[string]int {
    return map[string]int{}
}
// -----------------------------
// 改良後(帶錯誤訊息)
// -----------------------------
func AddItem(bill, units map[string]int, item, unit string) error {
    qty, ok := units[unit]
    if !ok {
        return fmt.Errorf("不存在的單位: %s", unit)
    }
    bill[item] += qty
    return nil
}
func RemoveItem(bill, units map[string]int, item, unit string) error {
    qty, ok := units[unit]
    if !ok {
        return fmt.Errorf("不存在的單位: %s", unit)
    }
    current, exists := bill[item]
    if !exists {
        return fmt.Errorf("帳單上沒有 %s", item)
    }
    if current < qty {
        return fmt.Errorf("退貨數量太多 (現有 %d, 退 %d)", current, qty)
    }
    if current == qty {
        delete(bill, item)
    } else {
        bill[item] -= qty
    }
    return nil
}
func GetItem(bill map[string]int, item string) (int, error) {
    qty, ok := bill[item]
    if !ok {
        return 0, fmt.Errorf("帳單上沒有 %s", item)
    }
    return qty, nil
}
// 小幫手
func CheckErr(err error) {
    if err != nil {
        log.Println("❌", err)
    }
}
// -----------------------------
// 主程式
// -----------------------------
func main() {
    units := Units()
    bill := NewBill()
    fmt.Println("== 加入一打蘋果 ==")
    err := AddItem(bill, units, "apple", "dozen")
    CheckErr(err)
    fmt.Println("bill:", bill)
    fmt.Println("== 試著加入不存在的單位 ==")
    err = AddItem(bill, units, "orange", "mega_gross")
    CheckErr(err)
    fmt.Println("== 退貨比買的還多 ==")
    err = RemoveItem(bill, units, "apple", "great_gross")
    CheckErr(err)
    fmt.Println("== 查詢不存在的商品 ==")
    _, err = GetItem(bill, "grape")
    CheckErr(err)
}
== 加入一打蘋果 ==
bill: map[apple:12]
== 試著加入不存在的單位 ==
❌ 不存在的單位: mega_gross
== 退貨比買的還多 ==
❌ 退貨數量太多 (現有 12, 退 1728)
== 查詢不存在的商品 ==
❌ 帳單上沒有 grape