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

مجموعات متعددة الأشكال

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

يعد تخزين أنواع مختلفة في نفس المجموعة أحد أقوى تطبيقات الواجهات (interfaces). يمكن لشريحة (slice) من نوع واجهة أن تحتوي على أي قيمة تحقق تلك الواجهة، مما يسمح لك بتجميع أنواع مختلفة ولكنها مرتبطة معاً.

فكر في سيناريو تحتاج فيه إلى إدارة أشكال مختلفة. بدلاً من استخدام شرائح (slices) منفصلة لكل نوع، يمكنك استخدام شريحة واحدة من نوع الواجهة (interface type):

type Shape interface {
    Area() float64
}

type Circle struct{ Radius float64 }
func (c Circle) Area() float64 { return 3.14159 * c.Radius * c.Radius }

type Rectangle struct{ Width, Height float64 }
func (r Rectangle) Area() float64 { return r.Width * r.Height }

func main() {
    shapes := []Shape{
        Circle{Radius: 5},
        Rectangle{Width: 4, Height: 3},
        Circle{Radius: 2},
    }
    
    for _, s := range shapes {
        fmt.Printf("Area: %.2f\n", s.Area())
    }
}

تحتوي شريحة shapes على كل من الدوائر والمستطيلات. عند التكرار، يستجيب كل عنصر لـ Area() وفقاً لنوعه الفعلي. هذا هو تعدد الأشكال المطبق على المجموعات—حيث تتعامل نفس الحلقة مع جميع أنواع الأشكال بشكل موحد.

تعد المجموعات متعددة الأشكال (Polymorphic collections) أساسية عند بناء أنظمة تعالج عناصر متنوعة: مثل نظام إشعارات يرسل رسائل بريد إلكتروني ورسائل SMS، أو لعبة تقوم بتحديث أنواع مختلفة من الأعداء، أو معالج مستندات يتعامل مع تنسيقات ملفات متعددة. لا تهتم المجموعة بالأنواع المحددة، بل تهتم فقط بأن يوفر كل عنصر السلوك المطلوب.

challenge icon

التحدي

سهل

لنقم ببناء نظام لإدارة المهام يتعامل مع أنواع مختلفة من المهام من خلال واجهة موحدة. ستقوم بإنشاء أنواع مهام متنوعة ومعالجتها جميعاً معاً في مجموعة متعددة الأشكال (polymorphic collection).

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

  • task.go: قم بتعريف واجهة Task تتطلب تابعاً باسم Summary() string. ستكون هذه الواجهة هي العقد المشترك الذي يجب أن تلتزم به جميع أنواع المهام.
  • types.go: قم بإنشاء ثلاثة أنواع مختلفة من المهام، كل منها ينفذ واجهة Task:
    • BugFix مع حقول ID (string) و Severity (string)—وتابع Summary() الخاص به يعيد "Bug #[ID] ([Severity])"
    • Feature مع حقول Name (string) و Points (int)—وتابع Summary() الخاص به يعيد "Feature: [Name] - [Points] pts"
    • Documentation مع حقل Topic (string)—وتابع Summary() الخاص به يعيد "Docs: [Topic]"
  • main.go: قم بإنشاء دالة تسمى PrintBacklog تقبل شريحة (slice) من Task وتطبع ملخص كل مهمة في سطر مستقل. اقرأ تفاصيل المهام من المدخلات، وأنشئ واحداً من كل نوع من أنواع المهام، واجمعها جميعاً في شريحة []Task واحدة، ومررها إلى PrintBacklog.

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

  • السطر 1: معرف الخطأ (Bug ID)
  • السطر 2: خطورة الخطأ (Bug severity)
  • السطر 3: اسم الميزة (Feature name)
  • السطر 4: نقاط الميزة (Feature points - عدد صحيح)
  • السطر 5: موضوع التوثيق (Documentation topic)

على سبيل المثال، إذا كانت المدخلات "1042"، "critical"، "Dark Mode"، "8"، و "API Reference"، يجب أن يكون مخرجك:

Bug #1042 (critical)
Feature: Dark Mode - 8 pts
Docs: API Reference

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

ورقة مرجعية

يمكن لشريحة (slice) من نوع واجهة (interface) أن تحمل أي قيمة تحقق تلك الواجهة، مما يتيح إنشاء مجموعات متعددة الأشكال (polymorphic collections):

type Shape interface {
    Area() float64
}

type Circle struct{ Radius float64 }
func (c Circle) Area() float64 { return 3.14159 * c.Radius * c.Radius }

type Rectangle struct{ Width, Height float64 }
func (r Rectangle) Area() float64 { return r.Width * r.Height }

func main() {
    shapes := []Shape{
        Circle{Radius: 5},
        Rectangle{Width: 4, Height: 3},
        Circle{Radius: 2},
    }
    
    for _, s := range shapes {
        fmt.Printf("Area: %.2f\n", s.Area())
    }
}

تحتوي شريحة shapes على أنواع مختلفة (Circle و Rectangle) والتي تطبق جميعها واجهة Shape. عند التكرار، يستجيب كل عنصر لـ Area() وفقاً لنوعه الفعلي - وهذا هو تعدد الأشكال (polymorphism) المطبق على المجموعات.

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

جرّب بنفسك

package main

import (
	"fmt"
)

// TODO: قم بإنشاء دالة PrintBacklog تقبل slice من النوع Task
// وتطبع ملخص كل مهمة في سطر منفصل

func main() {
	// قراءة المدخلات
	var bugID string
	var bugSeverity string
	var featureName string
	var featurePoints int
	var docTopic string

	fmt.Scanln(&bugID)
	fmt.Scanln(&bugSeverity)
	fmt.Scanln(&featureName)
	fmt.Scanln(&featurePoints)
	fmt.Scanln(&docTopic)

	// TODO: قم بإنشاء واحدة من كل نوع من أنواع المهام (BugFix، Feature، Documentation)

	// TODO: اجمع كل المهام في slice واحدة من النوع []Task

	// TODO: قم باستدعاء PrintBacklog مع slice المهام
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming