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

الواجهة مقابل الفئة المجردة (Abstract Class)

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

تسمح لك كل من الواجهات (interfaces) والأصناف المجردة (abstract classes) بتعريف عقود يجب على الأصناف الأخرى اتباعها، لكنهما يخدمان أغراضاً مختلفة ولهما قدرات متميزة.

تحدد الـ واجهة (interface) ما يجب على الفئة (class) القيام به، دون أي تنفيذ (implementation). إنها بمثابة عقد صريح:

public interface IFlyable
{
    void Fly();
}

public class Bird : IFlyable
{
    public void Fly()
    {
        Console.WriteLine("Flapping wings");
    }
}

يمكن لـ الفئة المجردة (abstract class) أن توفر كلاً من عقد وتنفيذ مشترك. إنها بمثابة مخطط جزئي:

public abstract class Vehicle
{
    public string Brand { get; set; }
    
    public void StartEngine()
    {
        Console.WriteLine("Engine started");
    }
    
    public abstract void Drive();  // يجب تنفيذها
}

public class Car : Vehicle
{
    public override void Drive()
    {
        Console.WriteLine("Driving on roads");
    }
}

تكمن الاختلافات الرئيسية في قواعد الوراثة والغرض.

يمكن للفئة تنفيذ واجهات متعددة ولكن يمكنها الوراثة من فئة مجردة واحدة فقط.

استخدم الواجهة (interface) عندما تحتاج فئات (classes) غير مرتبطة إلى مشاركة سلوك معين (مثل IFlyable للطيور والطائرات). استخدم فئة مجردة (abstract class) عندما تشترك فئات مرتبطة في كود وحالة مشتركة (مثل المركبات التي تشترك في آلية تشغيل المحرك).

يمكن للأصناف من نوع Abstract classes أن تحتوي على constructors، و fields، و access modifiers على الأعضاء. بينما لم تكن الـ Interfaces تسمح بذلك تقليدياً، على الرغم من أن لغة C# الحديثة تسمح بـ default implementations.

عند الشك، فضّل استخدام الواجهات (interfaces) من أجل المرونة - يمكنك دائمًا إضافة فئة مجردة (abstract class) لاحقًا إذا أصبح التنفيذ المشترك ضروريًا.

challenge icon

التحدي

سهل

لنقم ببناء نظام مشغل وسائط يوضح متى يتم استخدام الواجهات (interfaces) مقابل الفئات المجردة (abstract classes). ستقوم بإنشاء تصميم حيث تشترك الأجهزة غير المرتبطة في سلوك التشغيل من خلال واجهة، بينما تشترك أنواع الوسائط المرتبطة في تنفيذ مشترك من خلال فئة مجردة.

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

  • IPlayable.cs: قم بتعريف واجهة تسمى IPlayable داخل مساحة الاسم Media. تمثل هذه الواجهة العقد لأي شيء يمكنه تشغيل الوسائط - سواء كان هاتفاً، أو تلفازاً، أو مشغل موسيقى مخصصاً. يجب أن تصرح عن ميثود واحدة Play() تعيد سلسلة نصية (string).
  • MediaFile.cs: قم بتعريف فئة مجردة تسمى MediaFile داخل مساحة الاسم Media. يمثل هذا الأساس المشترك لجميع ملفات الوسائط. يجب أن تحتوي على خاصية Title (string) وخاصية Duration (int، تمثل الثواني). قم بتضمين منشئ (constructor) يضبط كلتا القيمتين. أضف ميثود ملموسة تسمى GetInfo() تعيد "{Title} ({Duration}s)". قم أيضاً بالتصريح عن ميثود مجردة GetType() تعيد سلسلة نصية - حيث سيحدد كل نوع وسائط نوع الملف الخاص به.
  • AudioFile.cs: قم بتعريف فئة AudioFile داخل مساحة الاسم Media ترث من MediaFile وتنفذ IPlayable. أضف خاصية Artist (string). يجب أن يقبل المنشئ العنوان، والمدة، والفنان. قم بعمل override للميثود GetType() لتعيد "Audio". قم بتنفيذ Play() لتعيد "Playing audio: {Title} by {Artist}".
  • Program.cs: في ملفك الرئيسي، قم بإنشاء AudioFile باستخدام قيم الإدخال. استعرض كلا جانبي الفئة: استدعِ GetInfo() و GetType() (الموروثتين من الفئة المجردة)، ثم استدعِ Play() (من الواجهة). قم بتخزين ملف الصوت في متغير من نوع IPlayable واستدعِ Play() مرة أخرى لإظهار تعدد الأشكال للواجهة (interface polymorphism).

ستتلقى ثلاثة مدخلات:

  • عنوان الأغنية
  • المدة بالثواني
  • اسم الفنان

اطبع المخرجات بهذا التنسيق:

Info: {GetInfo() result}
Type: {GetType() result}
Direct: {Play() result}
Via Interface: {Play() result from IPlayable variable}

على سبيل المثال، إذا كانت المدخلات هي Bohemian Rhapsody، و 354، و Queen، يجب أن تكون المخرجات:

Info: Bohemian Rhapsody (354s)
Type: Audio
Direct: Playing audio: Bohemian Rhapsody by Queen
Via Interface: Playing audio: Bohemian Rhapsody by Queen

لاحظ كيف تستفيد AudioFile من كلا النهجين: فهي ترث التنفيذ المشترك (GetInfo()) من الفئة المجردة بينما تلتزم أيضاً بعقد IPlayable. توفر الفئة المجردة إعادة استخدام الكود لأنواع الوسائط المرتبطة، بينما تسمح الواجهة لأي فئة غير مرتبطة بأن تكون "قابلة للتشغيل" دون مشاركة تسلسل هرمي للوراثة.

ورقة مرجعية

تُعرف الواجهة (interface) عقداً يجب على الفئات اتباعه، دون أي تنفيذ:

public interface IFlyable
{
    void Fly();
}

public class Bird : IFlyable
{
    public void Fly()
    {
        Console.WriteLine("Flapping wings");
    }
}

توفر الفئة المجردة (abstract class) كلاً من العقد والتنفيذ المشترك:

public abstract class Vehicle
{
    public string Brand { get; set; }
    
    public void StartEngine()
    {
        Console.WriteLine("Engine started");
    }
    
    public abstract void Drive();  // Must be implemented
}

public class Car : Vehicle
{
    public override void Drive()
    {
        Console.WriteLine("Driving on roads");
    }
}

الاختلافات الرئيسية:

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

جرّب بنفسك

using System;
using Media;

class Program
{
    public static void Main(string[] args)
    {
        // قراءة قيم المدخلات
        string title = Console.ReadLine();
        int duration = Convert.ToInt32(Console.ReadLine());
        string artist = Console.ReadLine();
        
        // TODO: إنشاء AudioFile باستخدام قيم المدخلات
        
        // TODO: طباعة Info: متبوعة بنتيجة GetInfo()
        
        // TODO: طباعة Type: متبوعة بنتيجة GetType()
        
        // TODO: طباعة Direct: متبوعة بنتيجة Play()
        
        // TODO: تخزين ملف الصوت في متغير IPlayable
        
        // TODO: طباعة Via Interface: متبوعة بنتيجة Play() من متغير IPlayable
    }
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming