الانتقال إلى المحتوى الرئيسي
الوحدة 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

يعد التضمين أداة تفويض سهلة الاستخدام، وليس خيارًا للتصميم أفضل تمامًا من التركيب الصريح: استخدمه عندما تريد ذلك حقًا للترويج لواجهة برمجة تطبيقات النوع الداخلي.

جربه

تمرين#go.m5.l3.e1
المحاولات: 0جارٍ التحميل…

تعريف الكلب الذي يقوم بتضمين الحيوان (التضمين، بدون اسم حقل) ويحتوي على حقل سلسلة السلالة.

جارٍ تحميل المحرر…
إظهار التلميح

التضمين: اكتب النوع دون إعطائه اسم حقل.

الحل متاح بعد 3 من المحاولات

تمرين#go.m5.l3.e2
المحاولات: 0جارٍ التحميل…

أضف طريقة سلسلة Greet() إلى Animal واستدعها مباشرة على متغير Dog (ترويج الطريقة).

جارٍ تحميل المحرر…
إظهار التلميح

تمت ترقية الطريقة المحددة في Animal إلى Dog بفضل التضمين.

الحل متاح بعد 3 من المحاولات

Quiz#go.m5.l3.e3
جاهز

هل يدعم Go الميراث الكلاسيكي؟

Go
type B struct { /* extends A? */ }
خيارات الإجابة

خلاصة

  • لا وراثة: تؤلف مع التضمين.
  • التضمين = كتابة النوع بدون اسم حقل في البنية.
  • يتم ترقية الحقول والأساليب من النوع المضمن **.
  • الوصول الصريح متاح دائمًا: outer.Inner.Field.
  • التجاوز "المعجمي": طريقة النوع الخارجي تظلل الطريقة التي يتم الترويج لها.
  • التضمين ≠ is-a: لتعدد الأشكال، استخدم الواجهات.