دروس الوحدة (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يعد التضمين أداة تفويض سهلة الاستخدام، وليس خيارًا للتصميم أفضل تمامًا من التركيب الصريح: استخدمه عندما تريد ذلك حقًا للترويج لواجهة برمجة تطبيقات النوع الداخلي.
جربه
تعريف الكلب الذي يقوم بتضمين الحيوان (التضمين، بدون اسم حقل) ويحتوي على حقل سلسلة السلالة.
إظهار التلميح
التضمين: اكتب النوع دون إعطائه اسم حقل.
الحل متاح بعد 3 من المحاولات
أضف طريقة سلسلة Greet() إلى Animal واستدعها مباشرة على متغير Dog (ترويج الطريقة).
إظهار التلميح
تمت ترقية الطريقة المحددة في Animal إلى Dog بفضل التضمين.
الحل متاح بعد 3 من المحاولات
هل يدعم Go الميراث الكلاسيكي؟
type B struct { /* extends A? */ }خلاصة
- لا وراثة: تؤلف مع التضمين.
- التضمين = كتابة النوع بدون اسم حقل في البنية.
- يتم ترقية الحقول والأساليب من النوع المضمن **.
- الوصول الصريح متاح دائمًا:
outer.Inner.Field. - التجاوز "المعجمي": طريقة النوع الخارجي تظلل الطريقة التي يتم الترويج لها.
- التضمين ≠ is-a: لتعدد الأشكال، استخدم الواجهات.