דילוג לתוכן המרכזי
מודול 5 · שיעור 3 מתוך 523/50 בקורס~12 min
שיעורי מודול (3/5)

קומפוזיציה (הטמעה)

לגו אין ירושה. התשובה האידיומטית לשימוש חוזר היא הרכב, ובפרט הטבעה: כולל סוג אחד בתוך אחר מבלי לתת לו שם שדה. התחומים והשיטות של הסוג המוטבע מקבל קידום, והופך לנגיש ישירות כאילו הם השתייכו לסוג החיצוני.

הטבעת מבנה

Go
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. זה נחוץ מתי יש התנגשויות.

ביטול (חלקי): שיטה "הצללה"

אם הסוג החיצוני מגדיר שיטה באותו שם כמו הקודמת האחד, השיטה שלו "מנצחת". מבפנים אתה תמיד יכול להגיע למשובץ אחד דרך שם הסוג:

Go
func (c Dog) Greet() string {
    return "BAU! " + c.Animal.Greet()
}

זו לא ירושה פולימורפית: זו רק רזולוציית שם מילוני. לפולימורפיזם אתה משתמש בממשקים (מודול 6).

הטבעה מרובה

ניתן להטמיע מספר סוגים:

Go
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):

Go
type ReadWriter interface {
    io.Reader
    io.Writer
}

זה שווה ערך לאיחוד השיטות של Reader וWriter.

מתי להשתמש בהרכב מפורש (לא הטמעה)

אם אתה רוצה שדה עם שם וללא קידום אוטומטי, השתמש ברגיל הרכב:

Go
type Dog struct {
    Anim  Animal    // NORMAL field, no promotion
    Breed string
}
c.Anim.Name   // you need the field name

הטבעה היא כלי האצלה שימושי, לא בחירה עיצובית עדיף בהחלט מאשר קומפוזיציה מפורשת: השתמש בו מתי שאתה באמת רוצה כדי לקדם את ה-API של הסוג הפנימי.

נסה את זה

פעילות גופנית#go.m5.l3.e1
ניסיונות: 0טוען...

הגדר כלב שמטמיע בעלי חיים (הטבעה, ללא שם שדה) ויש לו שדה מחרוזת גזע.

טוען עורך...
הצג רמז

הטבעה: כתוב את ה-TYPE מבלי לתת לו שם שדה.

הפתרון זמין לאחר 3 ניסיונות

פעילות גופנית#go.m5.l3.e2
ניסיונות: 0טוען...

הוסף שיטת מחרוזת Greet() ל- Animal וקרא לה ישירות על משתנה Dog (קידום שיטה).

טוען עורך...
הצג רמז

השיטה המוגדרת ב- Animal מקודמת לכלב הודות להטמעה.

הפתרון זמין לאחר 3 ניסיונות

Quiz#go.m5.l3.e3
מוכן

האם Go תומך בירושה קלאסית?

Go
type B struct { /* extends A? */ }
אפשרויות תשובה

לְסַכֵּם

  • ללא ירושה: אתה מלחין עם הטבעה.
  • Embedding = כתיבת ה-TYPE ללא שם שדה ב-struct.
  • שדות ושיטות מהסוג המוטבע מקבלים קידום.
  • גישה מפורשת זמינה תמיד: outer.Inner.Field.
  • עקיפה "לקסיקלית": השיטה של ​​הטיפוס החיצוני מאפילה על המקודם.
  • הטמעה ≠ is-a: לפולימורפיזם השתמש בממשקים.