أساسيات تضمين الـ Struct
جزء من قسم Object Oriented Programming في رحلة GO على Coddy — الدرس 35 من 107.
تضمين الهياكل (Struct embedding) هو آلية لغة Go لتضمين نوع struct واحد داخل نوع آخر. بدلاً من استخدام اسم حقل، تقوم ببساطة بتحديد النوع، وتقوم Go بترقية حقول الـ struct المضمن إلى الـ struct الخارجي.
إليك الصيغة الأساسية:
type Address struct {
City string
Country string
}
type Person struct {
Name string
Address // مضمن - لا يوجد اسم حقل
}
عندما تقوم بتضمين Address في Person، يمكنك الوصول إلى الحقول المضمنة مباشرة من الهيكل الخارجي (outer struct):
func main() {
p := Person{
Name: "Alice",
Address: Address{
City: "London",
Country: "UK",
},
}
fmt.Println(p.City) // London - وصول مباشر
fmt.Println(p.Country) // UK - وصول مباشر
fmt.Println(p.Address.City) // يعمل أيضاً
}
لاحظ كيف يعمل p.City دون كتابة p.Address.City بشكل صريح. تقوم لغة Go تلقائياً بترقية حقول الهيكل المضمن (embedded struct)، مما يجعل الوصول إليها ممكناً كما لو كانت تنتمي إلى الهيكل الخارجي. لا يزال بإمكانك الوصول إليها من خلال اسم النوع المضمن عند الحاجة.
يختلف هذا عن الهياكل المتداخلة (nested structs) العادية حيث يجب عليك دائمًا استخدام اسم الحقل للوصول إلى الحقول الداخلية. يُنشئ التضمين (Embedding) واجهة برمجة تطبيقات (API) أكثر تسطحًا وملاءمة مع الاستمرار في الحفاظ على التجميع المنطقي للبيانات ذات الصلة.
التحدي
سهللنقم ببناء نظام لتسجيل المركبات يستخدم تضمين الهياكل (struct embedding) لإنشاء نموذج بيانات نظيف ومنظم. ستقوم بفصل معلومات الاتصال عن تفاصيل المركبة مع السماح بالوصول المباشر إلى جميع الحقول من خلال التضمين.
ستقوم بتنظيم الكود الخاص بك عبر ملفين:
vehicle.go: قم بإنشاء هيكلين (structs) يعملان معاً من خلال التضمين:- هيكل
ContactInfoيحتوي على حقولOwnerNameوPhone(كلاهما سلاسل نصية strings) - هيكل
VehicleيضمنContactInfo(بدون اسم حقل) ويضيف حقوله الخاصةMake،Model، وYear
SummaryعلىVehicleتعيد سلسلة نصية منسقة تحتوي على جميع معلومات المركبة والاتصال.- هيكل
main.go: اقرأ تفاصيل المركبة والمالك من المدخلات، وأنشئ مثيلاً (instance) منVehicleمعContactInfoالمضمنة، ووضح كيفية الوصول إلى الحقول المضمنة مباشرة ومن خلال اسم النوع المضمن. اطبع ملخص المركبة في النهاية.
سيتم توفير المدخلات التالية:
- السطر 1: اسم المالك
- السطر 2: رقم الهاتف
- السطر 3: ماركة المركبة
- السطر 4: طراز المركبة
- السطر 5: السنة (عدد صحيح integer)
يجب أن تعيد دالة Summary سلسلة نصية بهذا التنسيق:
[Year] [Make] [Model] - Owner: [OwnerName], Phone: [Phone]في ملفك الرئيسي، وضح ترقية الحقول (field promotion) من خلال طباعة:
- اسم المالك الذي يتم الوصول إليه مباشرة من المركبة (مثلاً،
v.OwnerName) - الهاتف الذي يتم الوصول إليه من خلال النوع المضمن (مثلاً،
v.ContactInfo.Phone) - الملخص الكامل من دالة
Summaryالخاصة بك
على سبيل المثال، إذا كانت المدخلات Alice Smith، 555-1234، Toyota، Camry، و 2023، يجب أن يكون مخرجك:
Alice Smith
555-1234
2023 Toyota Camry - Owner: Alice Smith, Phone: 555-1234لاحظ كيف يعمل كلا نمطي الوصول — الوصول المباشر من خلال ترقية الحقول والوصول الصريح من خلال اسم النوع المضمن — مما يمنحك مرونة في كيفية التعامل مع البيانات المضمنة.
ورقة مرجعية
يسمح تضمين الهياكل (Struct embedding) بتضمين نوع هيكل داخل آخر دون تحديد اسم حقل. يتم ترقية حقول الهيكل المضمن إلى الهيكل الخارجي، مما يجعل الوصول إليها مباشراً.
الصيغة الأساسية
type Address struct {
City string
Country string
}
type Person struct {
Name string
Address // embedded - no field name
}
الوصول إلى الحقول المضمنة
يمكنك الوصول إلى الحقول المضمنة بطريقتين:
p := Person{
Name: "Alice",
Address: Address{
City: "London",
Country: "UK",
},
}
// وصول مباشر (ترقية الحقول)
fmt.Println(p.City) // London
// من خلال اسم النوع المضمن
fmt.Println(p.Address.City) // London
تقوم لغة Go تلقائياً بترقية حقول الهيكل المضمن، مما يسمح بالوصول المباشر إليها كما لو كانت تنتمي إلى الهيكل الخارجي. لا يزال بإمكانك الوصول إليها من خلال اسم النوع المضمن عند الحاجة.
جرّب بنفسك
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// قراءة اسم المالك
ownerName, _ := reader.ReadString('\n')
ownerName = strings.TrimSpace(ownerName)
// قراءة رقم الهاتف
phone, _ := reader.ReadString('\n')
phone = strings.TrimSpace(phone)
// قراءة ماركة المركبة
make, _ := reader.ReadString('\n')
make = strings.TrimSpace(make)
// قراءة طراز المركبة
model, _ := reader.ReadString('\n')
model = strings.TrimSpace(model)
// قراءة السنة
yearStr, _ := reader.ReadString('\n')
yearStr = strings.TrimSpace(yearStr)
year, _ := strconv.Atoi(yearStr)
// TODO: إنشاء مثيل Vehicle مع ContactInfo مدمجة
// استخدم القيم المقروءة من المدخلات أعلاه
// TODO: طباعة اسم المالك الذي يتم الوصول إليه مباشرة من Vehicle (field promotion)
// Example: fmt.Println(v.OwnerName)
// TODO: طباعة الهاتف الذي يتم الوصول إليه من خلال اسم النوع المدمج
// Example: fmt.Println(v.ContactInfo.Phone)
// TODO: طباعة الملخص الكامل باستخدام التابع Summary
// Example: fmt.Println(v.Summary())
// قم بإزالة هذه الأسطر بمجرد تنفيذ الحل
_ = ownerName
_ = phone
_ = make
_ = model
_ = year
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP في Go
الملفات الخارجيةمساحة العمل والـ Modules في Goالـ Packages والـ Importsالأسماء المصدرة مقابل غير المصدرةمقدمة إلى الـ OOP في Goالـ Structs كـ Classesتعريف الـ Methods في الـ Structsالـ Pointer Receivers مقابل الـ Value Receiversتهيئة الـ Structدوال الـ Constructorمراجعة - آلة حاسبة بسيطة4الواجهات
مقدمة في الواجهاتالتنفيذ الضمنيالواجهة كعقدالواجهة الفارغة (any)تأكيد النوع (Type Assertion)تبديل النوع (Type Switch)تركيب الواجهاتواجهات Stringer و Errorمراجعة - حاسبة الأشكال7التغليف
الحقول المصدرة وغير المصدرةالتغليف على مستوى الحزمةدوال الـ Getter والـ Setterإخفاء المعلومات في Goملخص - سجلات الطلاب10الأنواع العامة (Go 1.18+)
مقدمة في الأنواع العامةمعاملات الأنواعقيود الأنواعالـ Structs العامةحل بديل للـ Methods العامةملخص - المجموعات العامة13أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Abstract Factoryنمط Observerنمط Strategy2تعمق في الأنواع و Structs
الأنواع الأساسية والمركبةتعريفات الأنواع المخصصةStruct TagsStructs مجهولةStructs متداخلةالقيم الصفرية والافتراضيةمراجعة - دفتر العناوين5التركيب بدلاً من الوراثة
لماذا لا تدعم Go الوراثةأساسيات تضمين الـ Structترقية الـ Methodتضمين عدة Structsالتضمين مقابل التجميعحجب الـ Methods المضمنةملخص - الهيكل الهرمي للموظفين8معالجة الأخطاء و OOP
واجهة errorأنواع الأخطاء المخصصةتغليف الأخطاء (fmt.Errorf)أخطاء Sentinelerrors.Is() و errors.As()Panic و Defer و Recoverملخص - File Parser11المكتبة القياسية والبرمجة كائنية التوجه (OOP)
io.Reader و io.Writerواجهة sort.Interfaceواجهة fmt.Stringerencoding/json مع Structsواجهة http.Handlerمراجعة - نماذج REST API14أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبMiddleware كنمط مزين3المؤشرات والذاكرة
أساسيات المؤشرات في Goالمؤشرات إلى الـ Structsالتمرير بالقيمة مقابل التمرير بالمرجعدالة ()newGarbage Collection في Goملخص - بناء Linked List6تعدد الأشكال في Go
تعدد الأشكال عبر InterfacesDuck Typing في Goقواعد استيفاء الـ Interfaceمجموعات متعددة الأشكالحقن التبعيةمراجعة - معالج الدفع9التزامن والبرمجة كائنية التوجه (OOP)
أساسيات الـ Goroutinesالقنوات (Channels) والاتصالالقنوات المخزنة (Buffered) مقابل غير المخزنةجملة Selectsync.Mutex و sync.RWMutexsync.WaitGroupتصميم الـ Structs الآمنة للخيوط (Thread-Safe)مراجعة - Worker Pool