ממשקי Stringer ו-Error
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 32 מתוך 107.
הספרייה הסטנדרטית של Go מגדירה שני ממשקים שתשתמש בהם ללא הרף: fmt.Stringer ו-error. שניהם ממשקים פשוטים בעלי מתודה אחת, שמעניקים לטיפוסים שלך שילוב עוצמתי עם הפונקציונליות המובנית של Go.
הממשק Stringer נמצא בחבילה fmt ודורש שיטה אחת בלבד:
type Stringer interface {
String() string
}כשאתה מממש את String() בטיפוס שלך, פונקציות כמו fmt.Println משתמשות בו אוטומטית כדי להציג את הערך שלך:
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}
func main() {
p := Person{Name: "Alice", Age: 30}
fmt.Println(p) // Alice (בת 30)
}הממשק שגיאה פשוט באותה מידה, והוא דורש מתודה Error() שמחזירה מחרוזת:
type error interface {
Error() string
}כל טיפוס עם מתודה Error() יכול לשמש כערך שגיאה ב-Go. כך אפשר ליצור טיפוסי שגיאה מותאמים אישית עם הקשר נוסף:
type ValidationError struct {
Field string
Message string
}
func (e ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
func main() {
err := ValidationError{Field: "email", Message: "invalid format"}
fmt.Println(err) // email: invalid format
}שני הממשקים האלה מדגימים את הפילוסופיה של Go: ממשקים קטנים וממוקדים שמאפשרים התנהגות רבת עוצמה באמצעות מימוש משתמע.
אתגר
קלבואו נבנה מערכת לניטור טמפרטורה שמשתמשת גם בממשקים Stringer וגם error כדי לספק פלט קריא וטיפול משמעותי בשגיאות.
תארגנו את הקוד בשני קבצים:
temperature.go: צרו מבנהTemperatureעם השדותValue(float64) ו-Unit(string). ממשו את המתודהString()כך שבעת הדפסה היא תציג את הטמפרטורה בפורמט קריא. צרו גם מבנהTemperatureErrorעם השדותTemp(float64) ו-Reason(string), וממשו את המתודהError()כדי לספק הודעות שגיאה תיאוריות.main.go: צרו פונקציה בשםValidateTemperatureשמקבלתTemperatureומחזירה שגיאה אם הטמפרטורה אינה תקינה. טמפרטורה אינה תקינה אם היא נמוכה מאפס מוחלט (-273.15 עבור צלזיוס, -459.67 עבור פרנהייט). אם הטמפרטורה תקינה, הפונקציה מחזירהnil. קראו נתוני טמפרטורה מהקלט, צרו מופע של Temperature, אמתו אותו והדפיסו פלט מתאים.
הקלטים הבאים יסופקו:
- שורה 1: ערך הטמפרטורה (float)
- שורה 2: יחידה (
Cעבור צלזיוס אוFעבור פרנהייט)
המתודה String() שלכם צריכה להחזיר את הפורמט:
[Value] degrees [Unit]כאשר Unit היא המילה המלאה "Celsius" או "Fahrenheit", בהתאם לקלט.
המתודה Error() שלכם צריכה להחזיר את הפורמט:
invalid temperature [Temp]: [Reason]אם הטמפרטורה תקינה, הדפיסו את Temperature באמצעות fmt.Println (שישתמש במתודה String() שלכם). אם היא אינה תקינה, הדפיסו את השגיאה (שתשתמש במתודה Error() שלכם).
לדוגמה, עבור 25.5 ו-C, הפלט שלכם צריך להיות:
25.5 degrees Celsiusעבור -500 ו-F, הפלט שלכם צריך להיות:
invalid temperature -500: below absolute zeroהשתמשו ב-"below absolute zero" בתור הסיבה כאשר הטמפרטורה נמוכה מדי.
נסו בעצמכם
package main
import (
"fmt"
)
// ValidateTemperature בודקת אם הטמפרטורה תקינה
// מחזירה שגיאה אם הטמפרטורה נמוכה מהאפס המוחלט
// האפס המוחלט: -273.15 בסולם צלזיוס, -459.67 בסולם פרנהייט
func ValidateTemperature(t Temperature) error {
// TODO: יש לממש את לוגיקת האימות
// בדקו אם הטמפרטורה נמוכה מהאפס המוחלט בהתאם ליחידת המידה
// החזירו TemperatureError אם הטמפרטורה אינה תקינה, ו־nil אם היא תקינה
return nil
}
func main() {
var value float64
var unit string
fmt.Scanln(&value)
fmt.Scanln(&unit)
// TODO: צרו מבנה Temperature עם ערכי הקלט
// TODO: אמתו את הטמפרטורה באמצעות ValidateTemperature
// TODO: אם יש שגיאה, הדפיסו אותה
// אחרת, הדפיסו את הטמפרטורה (תוך שימוש במתודה String())
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
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 אונליין