העברה לפי ערך לעומת לפי הפניה
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 21 מתוך 107.
Go היא שפה שבה מעבירים לפי ערך. כשמעבירים משתנה לפונקציה, Go יוצרת עותק של הערך הזה. פירוש הדבר הוא ששינויים בתוך הפונקציה אינם משפיעים על המשתנה המקורי.
func double(n int) {
n = n * 2
}
func main() {
x := 10
double(x)
fmt.Println(x) // 10 (ללא שינוי)
}הדבר נכון גם לגבי מבנים. העברת מבנה לפונקציה מעתיקה את כל השדות שלו, ולכן שינויים בתוך הפונקציה אובדים.
type Person struct {
Name string
Age int
}
func birthday(p Person) {
p.Age++
}
func main() {
alice := Person{Name: "Alice", Age: 30}
birthday(alice)
fmt.Println(alice.Age) // 30 (ללא שינוי)
}כדי לשנות את הערך המקורי, העבר במקום זאת מצביע. המצביע עצמו מועתק, אבל הוא עדיין מצביע לאותה כתובת בזיכרון, כך שניתן לשנות את הנתונים המקוריים.
func birthday(p *Person) {
p.Age++
}
func main() {
alice := Person{Name: "Alice", Age: 30}
birthday(&alice)
fmt.Println(alice.Age) // 31 (שונה!)
}השתמשו במצביעים כשצריך לשנות את הערך המקורי או כשמעבירים מבנים גדולים כדי להימנע מהעתקה יקרה. השתמשו בערכים כשרוצים להגן על הנתונים המקוריים מפני שינויים לא מכוונים.
אתגר
קלבואו נבנה מערכת עגלת קניות שמדגימה את ההבדל בין העברת מבנים לפי ערך לבין העברתם באמצעות מצביע. תוכלו לראות במו עיניכם שרק פרמטרים של מצביע יכולים לשנות את הנתונים המקוריים.
תארגנו את הקוד שלכם בשני קבצים:
cart.go: הגדירו מבנהCartItemעם השדותName(מחרוזת),Price(float64) ו-Quantity(int). צרו שתי פונקציות שמנסות לעדכן את הכמות:UpdateQuantityByValueמקבלתCartItem(לפי ערך) וכמות חדשה, ואז קובעת את הכמות של הפריט לערך החדשUpdateQuantityByPointerמקבלת*CartItem(מצביע) וכמות חדשה, ואז קובעת את הכמות של הפריט לערך החדש
TotalעבורCartItem, שמחזירה את המחיר כפול הכמות.main.go: קראו את פרטי הפריט מהקלט, צרוCartItem, ואז הדגימו את שתי דרכי העדכון. תחילה נסו לעדכן באמצעות פונקציית הערך והציגו את התוצאה, ואז עדכנו באמצעות פונקציית המצביע והציגו שוב את התוצאה.
הקלט הבא יסופק:
- שורה 1: שם הפריט
- שורה 2: מחיר (מספר עשרוני)
- שורה 3: כמות התחלתית (מספר שלם)
- שורה 4: הכמות החדשה שיש לקבוע (מספר שלם)
מבנה הפלט:
Initial: [Name] x[Quantity] = $[Total]
After value update: [Name] x[Quantity] = $[Total]
After pointer update: [Name] x[Quantity] = $[Total]לדוגמה, עבור Laptop, 999.99, 1 ו-3, הפלט שלכם אמור להיות:
Initial: Laptop x1 = $999.99
After value update: Laptop x1 = $999.99
After pointer update: Laptop x3 = $2999.97שימו לב שהכמות נשארת 1 לאחר העדכון לפי ערך (הפונקציה שינתה עותק), אך משתנה ל-3 לאחר העדכון באמצעות מצביע (הפונקציה שינתה את המקור). עצבו את הסכומים עם שתי ספרות אחרי הנקודה העשרונית.
נסו בעצמכם
package main
import (
"fmt"
)
func main() {
// קראו את הקלט
var name string
var price float64
var initialQty, newQty int
fmt.Scanln(&name)
fmt.Scanln(&price)
fmt.Scanln(&initialQty)
fmt.Scanln(&newQty)
// TODO: צרו CartItem עם ערכי הקלט
// TODO: הדפיסו את המצב ההתחלתי באמצעות הפורמט:
// fmt.Printf("Initial: %s x%d = $%.2f\n", ...)
// TODO: נסו לעדכן את הכמות באמצעות UpdateQuantityByValue
// לאחר מכן הדפיסו את התוצאה (שימו לב שהכמות לא תשתנה)
// TODO: עדכנו את הכמות באמצעות UpdateQuantityByPointer
// לאחר מכן הדפיסו את התוצאה (שימו לב שהכמות תשתנה)
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
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 אונליין