שיעורי מודול (3/5)
קומפוזיציה (הטמעה)
לגו אין ירושה. התשובה האידיומטית לשימוש חוזר היא הרכב, ובפרט הטבעה: כולל סוג אחד בתוך אחר מבלי לתת לו שם שדה. התחומים והשיטות של הסוג המוטבע מקבל קידום, והופך לנגיש ישירות כאילו הם השתייכו לסוג החיצוני.
הטבעת מבנה
type Animal struct {
Name string
}
func (a Animal) Greet() string {
return "ciao da " + a.Name
}
type Dog struct {
Animal // embedding — NO field name
Breed string
}
c := Dog{
Animal: Animal{Name: "Rex"},
Breed: "Pastore",
}
fmt.Println(c.Name) // "Rex" — promoted from Animal
fmt.Println(c.Greet()) // "ciao da Rex" — promoted method
fmt.Println(c.Breed) // own fieldהשדה המוטבע עדיין נגיש לפי שם הסוג:
c.Animal.Name פועל וזהה ל-c.Name. זה נחוץ מתי
יש התנגשויות.
ביטול (חלקי): שיטה "הצללה"
אם הסוג החיצוני מגדיר שיטה באותו שם כמו הקודמת האחד, השיטה שלו "מנצחת". מבפנים אתה תמיד יכול להגיע למשובץ אחד דרך שם הסוג:
func (c Dog) Greet() string {
return "BAU! " + c.Animal.Greet()
}זו לא ירושה פולימורפית: זו רק רזולוציית שם מילוני. לפולימורפיזם אתה משתמש בממשקים (מודול 6).
הטבעה מרובה
ניתן להטמיע מספר סוגים:
type Logger struct{ /*...*/ }
func (l Logger) Log(msg string) { /*...*/ }
type Timer struct{ /*...*/ }
func (t Timer) Now() time.Time { /*...*/ }
type Service struct {
Logger
Timer
Name string
}
s := Service{Name: "api"}
s.Log("start") // promoted from Logger
s.Now() // promoted from Timerאם לשני סוגים משובצים יש שיטה עם אותו שם, גישה ישירה
הופך להיות מעורפל והמהדר דורש את הצורה המפורשת
(s.Logger.Foo() או s.Timer.Foo()).
הטמעת ממשק
ניתן גם "להטמיע" ממשקים (ראה מודול 6):
type ReadWriter interface {
io.Reader
io.Writer
}זה שווה ערך לאיחוד השיטות של Reader וWriter.
מתי להשתמש בהרכב מפורש (לא הטמעה)
אם אתה רוצה שדה עם שם וללא קידום אוטומטי, השתמש ברגיל הרכב:
type Dog struct {
Anim Animal // NORMAL field, no promotion
Breed string
}
c.Anim.Name // you need the field nameהטבעה היא כלי האצלה שימושי, לא בחירה עיצובית עדיף בהחלט מאשר קומפוזיציה מפורשת: השתמש בו מתי שאתה באמת רוצה כדי לקדם את ה-API של הסוג הפנימי.
נסה את זה
הגדר כלב שמטמיע בעלי חיים (הטבעה, ללא שם שדה) ויש לו שדה מחרוזת גזע.
הצג רמז
הטבעה: כתוב את ה-TYPE מבלי לתת לו שם שדה.
הפתרון זמין לאחר 3 ניסיונות
הוסף שיטת מחרוזת Greet() ל- Animal וקרא לה ישירות על משתנה Dog (קידום שיטה).
הצג רמז
השיטה המוגדרת ב- Animal מקודמת לכלב הודות להטמעה.
הפתרון זמין לאחר 3 ניסיונות
האם Go תומך בירושה קלאסית?
type B struct { /* extends A? */ }לְסַכֵּם
- ללא ירושה: אתה מלחין עם הטבעה.
- Embedding = כתיבת ה-TYPE ללא שם שדה ב-struct.
- שדות ושיטות מהסוג המוטבע מקבלים קידום.
- גישה מפורשת זמינה תמיד:
outer.Inner.Field. - עקיפה "לקסיקלית": השיטה של הטיפוס החיצוני מאפילה על המקודם.
- הטמעה ≠ is-a: לפולימורפיזם השתמש בממשקים.