שגיאות Sentinel
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 55 מתוך 107.
שגיאת זקיף היא משתנה שגיאה מוגדר מראש ברמת החבילה, שמייצג מצב שגיאה מסוים. בניגוד ליצירת שגיאות חדשות בכל פעם באמצעות errors.New, שגיאות זקיף מוכרזות פעם אחת ונעשה בהן שימוש חוזר לאורך כל הקוד.
מגדירים שגיאות זקיף כמשתנים ברמת החבילה, בדרך כלל עם שמות שמתחילים ב־Err:
package user
import "errors"
var (
ErrNotFound = errors.New("user not found")
ErrInvalidEmail = errors.New("invalid email address")
ErrDuplicate = errors.New("user already exists")
)פונקציות מחזירות את השגיאות המוגדרות מראש האלה כאשר מתקיימים תנאים מסוימים:
func FindUser(id int) (*User, error) {
user, exists := users[id]
if !exists {
return nil, ErrNotFound
}
return user, nil
}היתרון של שגיאות זקיף הוא שהקוראים לפונקציה יכולים לבדוק תנאי שגיאה מסוימים על ידי השוואה ישירה של ערכי שגיאה:
user, err := FindUser(42)
if err == ErrNotFound {
fmt.Println("Creating new user...")
} else if err != nil {
fmt.Println("Unexpected error:", err)
}הספרייה הסטנדרטית של Go משתמשת באופן נרחב בשגיאות זקיף. לדוגמה, io.EOF מסמנת את סוף הקלט, ו-sql.ErrNoRows מציינת שתוצאת השאילתה ריקה. התבנית הזאת יוצרת חוזה ברור בין חבילות למשתמשים שלהן לגבי השגיאות שניתן לצפות להן.
עם זאת, בדיקות שוויון פשוטות נכשלות כשעוטפים שגיאות. בשיעור הבא תלמדו איך errors.Is() פותרת את הבעיה הזאת באמצעות בדיקת שרשרת השגיאות כולה.
אתגר
קלבואו נבנה מערכת לניהול מלאי שמשתמשת בשגיאות sentinel כדי לתקשר תנאי כשל ספציפיים. תגדירו משתני שגיאה ברמת החבילה, שהקוד הקורא יוכל לבדוק כדי לטפל כראוי בתרחישים שונים.
תארגנו את הקוד שלכם בשני קבצים:
inventory.go: צרו מערכת מלאי עם שגיאות sentinel מוגדרות מראש עבור מקרי כשל נפוצים.הגדירו שלוש שגיאות sentinel ברמת החבילה:
ErrItemNotFoundעם ההודעה"item not found in inventory"ErrOutOfStockעם ההודעה"item is out of stock"ErrInsufficientQuantityעם ההודעה"insufficient quantity available"
צרו מבנה
Inventoryשמכיל מפה של שמות פריטים והכמויות שלהם. ממשו את המתודות הבאות:NewInventory() *Inventory- יוצרת מלאי ריקAddItem(name string, quantity int)- מוסיפה פריט או מעדכנת את הכמות שלוGetQuantity(name string) (int, error)- מחזירה את הכמות של פריט, או אתErrItemNotFoundאם הוא לא קייםPurchase(name string, quantity int) error- מנסה לרכוש פריטים. החזירו אתErrItemNotFoundאם הפריט לא קיים, אתErrOutOfStockאם הכמות הנוכחית היא אפס, או אתErrInsufficientQuantityאם הכמות המבוקשת עולה על המלאי הזמין. במקרה של הצלחה, הפחיתו את הכמות במלאי והחזירו אתnil.
main.go: קראו את פעולות המלאי מהקלט והדגימו כיצד הקוד הקורא יכול לבדוק אם התקבלה שגיאת sentinel מסוימת באמצעות השוואת שוויון ישירה.קראו מהקלט את שם הפריט ואת כמות הרכישה. צרו מלאי, הוסיפו אליו את הפריט
"laptop"בכמות5, ולאחר מכן נסו לבצע את הרכישה. בהתאם לשגיאה שהוחזרה, הדפיסו הודעה מסוימת שמראה שזיהיתם איזו שגיאת sentinel התרחשה.
הקלטים הבאים יסופקו:
- שורה 1: שם הפריט לרכישה
- שורה 2: הכמות לרכישה
טפלו בתוצאה על ידי בדיקה איזו שגיאה מסוימת הוחזרה:
- אם
err == ErrItemNotFound: הדפיסוError: Item '[name]' does not exist in our inventory - אם
err == ErrOutOfStock: הדפיסוError: Item '[name]' is currently out of stock - אם
err == ErrInsufficientQuantity: הדפיסוError: Cannot purchase [quantity] units of '[name]' - not enough in stock - אם הפעולה הצליחה: הדפיסו
Successfully purchased [quantity] units of '[name]'
לדוגמה, עבור laptop ו-3, הפלט שלכם אמור להיות:
Successfully purchased 3 units of 'laptop'ועבור phone ו-2, הפלט שלכם אמור להיות:
Error: Item 'phone' does not exist in our inventoryועבור laptop ו-10, הפלט שלכם אמור להיות:
Error: Cannot purchase 10 units of 'laptop' - not enough in stockנסו בעצמכם
package main
import (
"fmt"
)
func main() {
// קראו את הקלט
var itemName string
var quantity int
fmt.Scanln(&itemName)
fmt.Scanln(&quantity)
// צרו מלאי והוסיפו את "laptop" בכמות 5
inv := NewInventory()
inv.AddItem("laptop", 5)
// TODO: נסו לרכוש את הפריט
// קראו ל־inv.Purchase(itemName, quantity)
// TODO: בדקו איזו שגיאת sentinel ספציפית הוחזרה באמצעות השוואת שוויון ישירה
// השתמשו במשפטי if/else if כדי לבדוק:
// - err == ErrItemNotFound
// - err == ErrOutOfStock
// - err == ErrInsufficientQuantity
// - err == nil (הצלחה)
// TODO: הדפיסו את ההודעה המתאימה בהתאם לשגיאה
// אם ErrItemNotFound: fmt.Printf("Error: Item '%s' does not exist in our inventory\n", itemName)
// אם ErrOutOfStock: fmt.Printf("Error: Item '%s' is currently out of stock\n", itemName)
// אם ErrInsufficientQuantity: fmt.Printf("Error: Cannot purchase %d units of '%s' - not enough in stock\n", quantity, itemName)
// אם הפעולה הצליחה: fmt.Printf("Successfully purchased %d units of '%s'\n", quantity, itemName)
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות OOP ב-Go
קבצים חיצונייםסביבת העבודה והמודולים של Goחבילות וייבואשמות מיוצאים לעומת שמות לא מיוצאיםמבוא ל-OOP ב-GoStructs כמחלקותהגדרת מתודות עבור Structsמקבלי Pointer לעומת Valueאתחול Structפונקציות Constructorחזרה – מחשבון פשוט4ממשקים
מבוא לממשקיםמימוש משתמעממשק כחוזהממשק ריק (any)אימות טיפוסמתג טיפוסיםהרכבת ממשקיםממשקי Stringer ו-Errorסיכום – מחשבון צורות7כימוס
שדות מיוצאים לעומת שדות לא מיוצאיםכימוס ברמת החבילהמתודות Getter ו-Setterהסתרת מידע ב-Goסיכום – רשומות תלמידים10Generics (Go 1.18 ומעלה)
מבוא ל־Genericsפרמטרים של טיפוסיםאילוצי טיפוסיםמבנים גנרייםפתרון עוקף למתודות גנריותסיכום – אוסף גנרי2העמקה בסוגים וב-Structs
סוגים בסיסיים ומורכביםהגדרות של סוגים מותאמים אישיתתגיות StructStructs אנונימייםStructs מקונניםערכי אפס וברירות מחדלסיכום – ספר אנשי קשר5קומפוזיציה במקום ירושה
למה ב-Go אין ירושהיסודות הטמעת Structקידום מתודותהטמעת כמה Structsהטמעה לעומת אגרגציההסתרת מתודות מוטמעותסיכום – היררכיית עובדים8טיפול בשגיאות ו-OOP
ממשק errorסוגי שגיאות מותאמים אישיתעטיפת שגיאות (fmt.Errorf)שגיאות Sentinelerrors.Is() ו-errors.As()Panic, Defer ו-Recoverסיכום – מנתח קבצים11ספרייה סטנדרטית ותכנות מונחה עצמים
io.Reader ו-io.Writersort.Interfaceהממשק fmt.Stringerencoding/json עם מבניםהממשק http.Handlerסיכום – מודלים של REST API3מצביעים וזיכרון
יסודות המצביעים ב-Goמצביעים למבניםהעברה לפי ערך לעומת לפי הפניההפונקציה new()איסוף אשפה ב-Goסיכום - בונה רשימה מקושרת6פולימורפיזם ב-Go
פולימורפיזם באמצעות ממשקיםטיפוס ברווז ב-Goכללי התאמה לממשקאוספים פולימורפייםהזרקת תלויותסיכום - מעבד תשלומים9מקביליות ו-OOP
יסודות GoroutinesChannels ותקשורתChannel עם חוצץ לעומת ללא חוצץפקודת Selectsync.Mutex ו-sync.RWMutexsync.WaitGroupתכנון Struct בטוח לשימוש במקבילסיכום - מאגר עובדים12מושגים מתקדמים בתכנות מונחה עצמים
דפוס האפשרויות הפונקציונליותדפוס Builder ב-Goשרשור מתודותכינויי טיפוסים לעומת הגדרותיסודות Reflectionסקירה כללית של יצירת קודתרגלו בעצמכם: קומפיילר Go אונליין