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

التركيب بدلاً من الوراثة

جزء من قسم Object Oriented Programming في رحلة C# على Coddy — الدرس 41 من 70.

يؤدي التوريث إلى إنشاء اقتران وثيق بين الفئات. عندما يرث Car من Vehicle، فإن أي تغيير في Vehicle يمكن أن يؤدي إلى تعطل Car. يوفر التركيب بديلاً أكثر مرونة: فبدلاً من وراثة السلوك، تحتوي الفئة على كائنات توفر ذلك السلوك.

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

public class MovementBehavior
{
    public void Move() => Console.WriteLine("Moving...");
}

public class AttackBehavior
{
    public void Attack() => Console.WriteLine("Attacking!");
}

public class Character
{
    private MovementBehavior movement = new MovementBehavior();
    private AttackBehavior attack = new AttackBehavior();
    
    public void Move() => movement.Move();
    public void Attack() => attack.Attack();
}

الفرق الجوهري هو العلاقة. تمثل الوراثة (Inheritance) علاقة "is-a" (الكلب هو حيوان)، بينما يمثل التركيب (Composition) علاقة "has-a" (الشخصية لديها سلوك حركة). يتيح لك التركيب تبديل السلوكيات في وقت التشغيل (runtime)، ودمج القدرات بحرية، وتجنب مشكلة الفئة الأساسية الهشة (fragile base class problem).

استخدم الوراثة عندما تكون هناك علاقة هرمية حقيقية. اختر التركيب عندما تحتاج إلى المرونة، أو عندما قد يتم مشاركة السلوكيات عبر فئات غير مرتبطة، أو عندما تريد تغيير السلوك ديناميكيًا.

challenge icon

التحدي

سهل

لنقم ببناء نظام إشعارات يوضح قوة التركيب (composition) على الوراثة (inheritance). بدلاً من إنشاء تسلسل هرمي معقد لفئات الإشعارات، ستقوم بتركيب NotificationSender من خلال تزويده بمكونات سلوك قابلة للتبديل.

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

  • Behaviors.cs: قم بإنشاء فئتي سلوك (behavior classes) في مساحة الأسماء Notifications تتعاملان مع جوانب مختلفة من إرسال الإشعارات:
    • FormattingBehavior - تحتوي على طريقة Format(string message) تعيد الرسالة مغلفة بـ [ALERT] في البداية و [END] في النهاية.
    • DeliveryBehavior - تحتوي على طريقة Deliver(string channel, string message) تعيد سلسلة نصية بالتنسيق Sending via {channel}: {message}.
  • NotificationSender.cs: قم بإنشاء فئة NotificationSender في مساحة الأسماء Notifications تستخدم التركيب للجمع بين السلوكيات. يجب أن يقوم المرسل الخاص بك بـ:
    • احتواء مثيلات (instances) من كل من FormattingBehavior و DeliveryBehavior كحقول خاصة (private fields).
    • امتلاك خاصية Channel (سلسلة نصية) تخزن اسم قناة التسليم.
    • امتلاك منشئ (constructor) يقبل اسم القناة.
    • امتلاك طريقة Send(string message) تقوم أولاً بتنسيق الرسالة باستخدام سلوك التنسيق، ثم تسليمها باستخدام سلوك التسليم، وتعيد النتيجة النهائية.
  • Program.cs: في ملفك الرئيسي، قم بإنشاء NotificationSender بقناة مأخوذة من المدخلات، ثم أرسل رسالة مأخوذة من المدخلات. اطبع النتيجة لتوضيح كيفية عمل السلوكيات المركبة معاً.

ستتلقى مدخلين:

  • اسم قناة التسليم (مثل Email، SMS)
  • الرسالة المراد إرسالها

اطبع نتيجة استدعاء Send() على مرسل الإشعارات الخاص بك.

على سبيل المثال، إذا كانت المدخلات هي Email و Server is down، يجب أن تكون المخرجات:

Sending via Email: [ALERT] Server is down [END]

لاحظ كيف أن NotificationSender لا يرث من أي شيء - إنه ببساطة "يمتلك" سلوكيات التنسيق والتسليم. يعني نهج التركيب هذا أنه يمكنك بسهولة تبديل أي من السلوكين دون تغيير فئة المرسل، أو إعادة استخدام هذه السلوكيات في فئات مختلفة تماماً!

ورقة مرجعية

التكوين (Composition) هو بديل للوراثة (inheritance) ينشئ كوداً أكثر مرونة من خلال جعل الفئة (class) تحتوي على كائنات توفر السلوك، بدلاً من وراثة السلوك من فئة أب (parent class).

الوراثة مقابل التكوين:

  • تمثل الوراثة علاقات "is-a" (الكلب هو حيوان)
  • يمثل التكوين علاقات "has-a" (الشخصية لديها سلوك حركة)

فوائد التكوين:

  • تبديل السلوكيات أثناء وقت التشغيل (runtime)
  • دمج القدرات بحرية
  • تجنب الاقتران الوثيق (tight coupling) ومشاكل الفئة الأساسية الهشة (fragile base class)
  • مشاركة السلوكيات عبر فئات غير مرتبطة

مثال مع مكونات السلوك:

public class MovementBehavior
{
    public void Move() => Console.WriteLine("Moving...");
}

public class AttackBehavior
{
    public void Attack() => Console.WriteLine("Attacking!");
}

public class Character
{
    private MovementBehavior movement = new MovementBehavior();
    private AttackBehavior attack = new AttackBehavior();
    
    public void Move() => movement.Move();
    public void Attack() => attack.Attack();
}

متى تستخدم كل نهج:

  • استخدم الوراثة للعلاقات الهرمية الحقيقية
  • اختر التكوين للمرونة، أو السلوكيات المشتركة، أو تغييرات السلوك الديناميكية

جرّب بنفسك

using System;
using Notifications;

class Program
{
    public static void Main(string[] args)
    {
        // قراءة المدخلات
        string channel = Console.ReadLine();
        string message = Console.ReadLine();

        // TODO: إنشاء NotificationSender مع القناة
        
        // TODO: إرسال الرسالة وطباعة النتيجة
        
    }
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming