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

نمط التركيب

جزء من قسم Object Oriented Programming في رحلة GO على Coddy — الدرس 96 من 107.

يتيح لك نمط التركيب (Composite pattern) التعامل مع الكائنات الفردية ومجموعات الكائنات بشكل موحد. فبينما يغير نمط الحالة (State) السلوك بناءً على الحالة الداخلية، يقوم نمط التركيب (Composite) ببناء هياكل شجرية تشترك فيها كل من العناصر الفردية (leaves) والحاويات (containers) في نفس الواجهة (interface).

هذا النمط مثالي للهياكل الهرمية مثل أنظمة الملفات، أو المخططات التنظيمية، أو مكونات واجهة المستخدم. في لغة Go، نقوم بتعريف واجهة (interface) مشتركة تنفذها كل من العناصر الفردية والحاويات:

type Component interface {
    GetSize() int
}

type File struct {
    Name string
    Size int
}

func (f File) GetSize() int {
    return f.Size
}

يحتوي المركب (الحاوية) على العناصر الأبناء ويقوم بتنفيذ نفس الواجهة من خلال تجميع نتائجهم:

type Folder struct {
    Name     string
    Children []Component
}

func (f *Folder) Add(c Component) {
    f.Children = append(f.Children, c)
}

func (f Folder) GetSize() int {
    total := 0
    for _, child := range f.Children {
        total += child.GetSize()
    }
    return total
}

الآن يمكنك تداخل المجلدات داخل المجلدات، واستدعاء GetSize() يعمل بشكل متطابق سواء كان ملفاً واحداً أو شجرة أدلة كاملة:

docs := &Folder{Name: "docs"}
docs.Add(File{Name: "readme.txt", Size: 100})
docs.Add(File{Name: "notes.txt", Size: 50})

root := &Folder{Name: "root"}
root.Add(docs)
root.Add(File{Name: "config.json", Size: 25})

fmt.Println(root.GetSize())  // 175

يعتبر النمط التركيبي (Composite) مثالياً عندما تحتاج إلى تمثيل التسلسلات الهرمية للجزء والكل، وتريد من العملاء معاملة الكائنات الفردية والتركيبات بشكل متماثل.

challenge icon

التحدي

سهل

لنقم ببناء نظام للهيكل التنظيمي باستخدام نمط Composite! ستقوم بإنشاء هيكل حيث يمكن معاملة كل من الموظفين الأفراد والأقسام (التي تحتوي على موظفين أو أقسام أخرى) بشكل موحد — وهو أمر مثالي لحساب إجمالي الرواتب في أي جزء من المنظمة.

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

  • component.go: تحديد الواجهة (interface) المشتركة التي سيقوم كل من الأفراد والمجموعات بتنفيذها.

    قم بإنشاء واجهة OrgComponent بطريقتين (methods):

    • GetSalary() int — تعيد إجمالي الراتب لهذا المكون
    • GetName() string — تعيد اسم الموظف أو القسم
  • organization.go: تنفيذ كل من أنواع الأوراق (الموظف الفردي) والمركبات (القسم).

    قم بإنشاء هيكل Employee مع حقول Name (string) و Salary (int). تعيد GetSalary() الخاصة به راتبه الخاص، وتعيد GetName() اسمه.

    قم بإنشاء هيكل Department مع حقل Name (string) وشريحة (slice) Members التي تحمل عناصر OrgComponent. أضف طريقة Add(c OrgComponent) لإضافة الأعضاء. يجب أن تعيد GetSalary() الخاصة به مجموع رواتب جميع الأعضاء، وتعيد GetName() اسم القسم.

  • main.go: بناء هيكل المنظمة وحساب الرواتب.

    اقرأ عدد الموظفين. لكل موظف، اقرأ اسمه وراتبه، وقم بإنشاء مثيلات Employee. ثم اقرأ عدد الأقسام. لكل قسم، اقرأ اسمه وعدد فهارس الأعضاء، متبوعاً بتلك الفهارس (تبدأ من 0، وتشير إلى الموظفين أو الأقسام التي تم إنشاؤها مسبقاً بترتيب إنشائها).

    بعد بناء الهيكل، اقرأ الفهرس النهائي واطبع اسم ذلك المكون وإجمالي راتبه بالتنسيق التالي: [name]: [salary]

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

  • عدد الموظفين، ثم اسم كل موظف وراتبه في أسطر منفصلة
  • عدد الأقسام، ثم اسم كل قسم، وعدد الأعضاء، وفهارس الأعضاء
  • الفهرس النهائي للاستعلام عنه

على سبيل المثال، إذا كان المعطى:

3
Alice
50000
Bob
60000
Carol
55000
1
Engineering
3
0
1
2
3

يجب أن يكون مخرجك:

Engineering: 165000

وإذا كان المعطى:

4
Alice
50000
Bob
60000
Carol
55000
Dave
70000
2
Dev
2
0
1
QA
2
2
3
4

يجب أن يكون مخرجك:

QA: 125000

وإذا كان المعطى:

2
Alice
50000
Bob
60000
0
1

يجب أن يكون مخرجك:

Bob: 60000

لاحظ كيف تعمل GetSalary() بشكل متطابق سواء قمت باستدعائها لموظف واحد أو لقسم بأكمله — يتيح لك نمط Composite معاملة الأفراد والمجموعات بشكل موحد من خلال نفس الواجهة!

ورقة مرجعية

يعامل نمط التركيب (Composite pattern) الكائنات الفردية ومجموعات الكائنات بشكل موحد من خلال واجهة مشتركة. إنه مثالي للهياكل الهرمية مثل أنظمة الملفات، أو المخططات التنظيمية، أو مكونات واجهة المستخدم.

قم بتعريف واجهة مشتركة تنفذها كل من العناصر الفردية والحاويات:

type Component interface {
    GetSize() int
}

تنفيذ الورقة (العنصر الفردي):

type File struct {
    Name string
    Size int
}

func (f File) GetSize() int {
    return f.Size
}

تنفيذ التركيب (الحاوية) التي تحتوي على أبناء وتجمع نتائجهم:

type Folder struct {
    Name     string
    Children []Component
}

func (f *Folder) Add(c Component) {
    f.Children = append(f.Children, c)
}

func (f Folder) GetSize() int {
    total := 0
    for _, child := range f.Children {
        total += child.GetSize()
    }
    return total
}

مثال على الاستخدام يوضح المعاملة الموحدة للعناصر الفردية والتكوينات:

docs := &Folder{Name: "docs"}
docs.Add(File{Name: "readme.txt", Size: 100})
docs.Add(File{Name: "notes.txt", Size: 50})

root := &Folder{Name: "root"}
root.Add(docs)
root.Add(File{Name: "config.json", Size: 25})

fmt.Println(root.GetSize())  // 175

جرّب بنفسك

package main

import "fmt"

func main() {
	// قراءة عدد الموظفين
	var numEmployees int
	fmt.Scanln(&numEmployees)

	// تخزين جميع المكونات (الموظفين والأقسام) بترتيب الإنشاء
	var components []OrgComponent

	// TODO: قراءة اسم وراتب كل موظف
	// إنشاء مثيلات Employee وإضافتها إلى شريحة components
	for i := 0; i < numEmployees; i++ {
		var name string
		var salary int
		fmt.Scanln(&name)
		fmt.Scanln(&salary)
		// TODO: إنشاء موظف وإضافته إلى components
	}

	// قراءة عدد الأقسام
	var numDepartments int
	fmt.Scanln(&numDepartments)

	// TODO: قراءة اسم كل قسم، وعدد الأعضاء، وفهارس الأعضاء
	// إنشاء مثيلات Department، وإضافة الأعضاء حسب الفهرس، وإضافتها إلى components
	for i := 0; i < numDepartments; i++ {
		var deptName string
		var memberCount int
		fmt.Scanln(&deptName)
		fmt.Scanln(&memberCount)
		// TODO: إنشاء قسم، وقراءة فهارس الأعضاء، وإضافة الأعضاء، وإضافتها إلى components
	}

	// قراءة الفهرس النهائي للاستعلام
	var queryIndex int
	fmt.Scanln(&queryIndex)

	// TODO: طباعة اسم المكون وإجمالي الراتب بالتنسيق: [name]: [salary]
	// مثال: fmt.Printf("%s: %d\n", name, salary)
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming