Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

أساسيات تضمين الـ 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) أكثر تسطحًا وملاءمة مع الاستمرار في الحفاظ على التجميع المنطقي للبيانات ذات الصلة.

challenge icon

التحدي

سهل

لنقم ببناء نظام لتسجيل المركبات يستخدم تضمين الهياكل (struct embedding) لإنشاء نموذج بيانات نظيف ومنظم. ستقوم بفصل معلومات الاتصال عن تفاصيل المركبة مع السماح بالوصول المباشر إلى جميع الحقول من خلال التضمين.

ستقوم بتنظيم الكود الخاص بك عبر ملفين:

  • vehicle.go: قم بإنشاء هيكلين (structs) يعملان معاً من خلال التضمين:
    • هيكل ContactInfo يحتوي على حقول OwnerName و Phone (كلاهما سلاسل نصية strings)
    • هيكل Vehicle يضمن ContactInfo (بدون اسم حقل) ويضيف حقوله الخاصة Make، Model، و Year
    قم أيضاً بإنشاء دالة (method) تسمى Summary على Vehicle تعيد سلسلة نصية منسقة تحتوي على جميع معلومات المركبة والاتصال.
  • main.go: اقرأ تفاصيل المركبة والمالك من المدخلات، وأنشئ مثيلاً (instance) من Vehicle مع ContactInfo المضمنة، ووضح كيفية الوصول إلى الحقول المضمنة مباشرة ومن خلال اسم النوع المضمن. اطبع ملخص المركبة في النهاية.

سيتم توفير المدخلات التالية:

  • السطر 1: اسم المالك
  • السطر 2: رقم الهاتف
  • السطر 3: ماركة المركبة
  • السطر 4: طراز المركبة
  • السطر 5: السنة (عدد صحيح integer)

يجب أن تعيد دالة Summary سلسلة نصية بهذا التنسيق:

[Year] [Make] [Model] - Owner: [OwnerName], Phone: [Phone]

في ملفك الرئيسي، وضح ترقية الحقول (field promotion) من خلال طباعة:

  1. اسم المالك الذي يتم الوصول إليه مباشرة من المركبة (مثلاً، v.OwnerName)
  2. الهاتف الذي يتم الوصول إليه من خلال النوع المضمن (مثلاً، v.ContactInfo.Phone)
  3. الملخص الكامل من دالة 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
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming