回顧 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