יסודות Reflection
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 83 מתוך 107.
התבוננות פנימית מאפשרת לתוכנית לבחון ולתפעל את המבנה של עצמה בזמן ריצה. החבילה reflect של Go מספקת את היכולת הזאת, ומאפשרת לך לבדוק טיפוסים, לקרוא שדות של מבנים ולקרוא למתודות באופן דינמי.
שני סוגי הליבה ברפלקציה הם reflect.Type ו־reflect.Value. מקבלים אותם באמצעות reflect.TypeOf() ו־reflect.ValueOf():
import "reflect"
type Person struct {
Name string
Age int
}
p := Person{Name: "Alice", Age: 30}
t := reflect.TypeOf(p)
v := reflect.ValueOf(p)
fmt.Println(t.Name()) // Person
fmt.Println(t.Kind()) // struct
fmt.Println(v.NumField()) // 2אפשר לעבור על השדות של המבנה כדי לבדוק את שמותיהם, טיפוסיהם וערכיהם:
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
fmt.Printf("%s: %v\n", field.Name, value)
}
// פלט:
// Name: Alice
// Age: 30רפלקציה היא עוצמתית, אך כרוכה בפשרות: היא עוקפת בדיקת טיפוסים בזמן הידור, פועלת לאט יותר מגישה ישירה ומקשה על הבנת הקוד. השתמשו בה במשורה למשימות כמו סריאליזציה ל-JSON, מיפוי ORM או בניית כלי עזר כלליים, שבהן הטיפוס אינו ידוע בזמן הידור.
אתגר
קלבואו נבנה בודק struct שמשתמש ברפלקציה כדי לבחון כל struct ולדווח על המבנה שלו! זהו שימוש מעשי ברפלקציה: יצירת כלי עזר שעובדים עם טיפוסים שאינם ידועים בזמן הקומפילציה.
תארגנו את הקוד שלכם בשני קבצים:
inspector.go: צרו כלי עזר לבדיקה המבוסס על רפלקציה.בנו פונקציה
InspectStruct(v any) stringשמשתמשת בחבילהreflectכדי לבחון כל struct שמועבר אליה. הפונקציה שלכם צריכה להחזיר דוח מעוצב הכולל:- שם הטיפוס של ה-struct
- ה-kind של ה-struct (אמור להיות "struct")
- מספר השדות
- עבור כל שדה: שם השדה, הטיפוס שלו והערך הנוכחי שלו
פורמט הפלט צריך להיות:
Type: [TypeName] Kind: struct Fields: [count] - [FieldName] ([FieldType]): [Value] - [FieldName] ([FieldType]): [Value] ...השתמשו ב-
reflect.TypeOf()כדי לקבל מידע על הטיפוס וב-reflect.ValueOf()כדי לגשת לערכי השדות. עברו על השדות באמצעותNumField(), באמצעותField(i)על הטיפוס כדי לקבל מטא-נתונים, ובאמצעותField(i)על הערך כדי לקבל את הערכים בפועל.main.go: הגדירו struct-ים לדוגמה והשתמשו בבודק שלכם.קראו טיפוס של struct (
productאוemployee) ואת ערכי השדות שלו, ואז צרו את ה-struct המתאים ובדקו אותו.עבור
product: צרו struct מסוגProductעם השדותName(מחרוזת),Price(float64) ו-InStock(בוליאני). קראו את השם, המחיר ומצב המלאי.עבור
employee: צרו struct מסוגEmployeeעם השדותID(מספר שלם),Name(מחרוזת) ו-Department(מחרוזת). קראו את המזהה, השם והמחלקה.העבירו את ה-struct שיצרתם אל
InspectStructוהדפיסו את התוצאה.
הקלטים הבאים יסופקו:
- שורה 1: טיפוס ה-struct (
productאוemployee) - השורות הבאות: ערכי השדות עבור טיפוס ה-struct הזה
לדוגמה, עבור הקלט:
product
Laptop
999.99
trueהפלט שלכם צריך להיות:
Type: Product
Kind: struct
Fields: 3
- Name (string): Laptop
- Price (float64): 999.99
- InStock (bool): trueועבור הקלט:
employee
42
Alice Johnson
Engineeringהפלט שלכם צריך להיות:
Type: Employee
Kind: struct
Fields: 3
- ID (int): 42
- Name (string): Alice Johnson
- Department (string): Engineeringועבור הקלט:
product
Headphones
79.50
falseהפלט שלכם צריך להיות:
Type: Product
Kind: struct
Fields: 3
- Name (string): Headphones
- Price (float64): 79.5
- InStock (bool): falseשימו לב שהבודק שלכם פועל עם טיפוסים שונים של struct בלי לדעת מראש מה המבנה שלהם. זהו כוחה של הרפלקציה! אותה פונקציה InspectStruct מטפלת באופן דינמי גם ב-struct מסוג Product וגם ב-struct מסוג Employee.
נסו בעצמכם
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// מבנה Product עם השדות Name, Price ו-InStock
type Product struct {
Name string
Price float64
InStock bool
}
// מבנה Employee עם השדות ID, Name ו-Department
type Employee struct {
ID int
Name string
Department string
}
func main() {
reader := bufio.NewReader(os.Stdin)
// קרא את סוג המבנה
structType, _ := reader.ReadString('\n')
structType = strings.TrimSpace(structType)
if structType == "product" {
// קרא את שדות המוצר
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
priceStr, _ := reader.ReadString('\n')
priceStr = strings.TrimSpace(priceStr)
price, _ := strconv.ParseFloat(priceStr, 64)
inStockStr, _ := reader.ReadString('\n')
inStockStr = strings.TrimSpace(inStockStr)
inStock, _ := strconv.ParseBool(inStockStr)
// TODO: צור מבנה Product עם הערכים שנקראו
// TODO: קרא ל-InspectStruct עם המוצר והדפס את התוצאה
} else if structType == "employee" {
// קרא את שדות העובד
idStr, _ := reader.ReadString('\n')
idStr = strings.TrimSpace(idStr)
id, _ := strconv.Atoi(idStr)
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
department, _ := reader.ReadString('\n')
department = strings.TrimSpace(department)
// TODO: צור מבנה Employee עם הערכים שנקראו
// TODO: קרא ל-InspectStruct עם העובד והדפס את התוצאה
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
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 אונליין