الواجهة مقابل الفئة المجردة (Abstract Class)
جزء من قسم البرمجة كائنية التوجه في رحلة C# على Coddy. الدرس 26 من 70.
تتيح لك كلٌّ من الواجهات والفئات المجردة Define عقودًا يجب على الفئات الأخرى اتباعها، لكن لكلٍّ منها أغراض مختلفة وإمكانات مميزة.
تحدّد interface ما يجب أن يفعله الصنف، من دون أي تنفيذ. إنها عقد خالص:
public interface IFlyable
{
void Fly();
}
public class Bird : IFlyable
{
public void Fly()
{
Console.WriteLine("Flapping wings");
}
}يمكن أن توفّر الفئة abstract عقدًا وتنفيذًا مشتركًا في آنٍ واحد. إنها مخطط جزئي:
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");
}
}تتمثل الاختلافات الرئيسية في قواعد الوراثة والغرض.
يمكن للفئة تنفيذ عدة واجهات، لكنها لا ترث إلا من فئة abstract واحدة.
استخدم واجهة عندما تحتاج الفئات غير المرتبطة إلى مشاركة السلوك (مثل IFlyable للطيور والطائرات). استخدم فئة مجردة عندما تشترك الفئات المرتبطة في تعليمات برمجية وحالة مشتركتين (مثل المركبات التي تشترك في آلية تشغيل المحرك).
يمكن أن تحتوي abstract class على constructor وحقول ومعدِّلات وصول للأعضاء. تقليديًا، لم يكن ذلك ممكنًا في interface، رغم أن C# الحديثة تسمح بالتطبيقات الافتراضية.
عند الشك، فضّل الواجهات لتحقيق المرونة - يمكنك دائمًا إضافة فئة abstract لاحقًا إذا أصبح التنفيذ المشترك ضروريًا.
التحدي
سهللنبنِ نظامًا لمشغّل وسائط يوضّح متى تستخدم الواجهات مقابل الفئات abstract. ستنشئ تصميمًا تشترك فيه الأجهزة غير المرتبطة في سلوك التشغيل من خلال interface، بينما تشترك أنواع الوسائط المرتبطة في تنفيذ مشترك من خلال فئة abstract.
ستنظّم التعليمات البرمجية في أربعة ملفات:
IPlayable.cs: عرّف interface باسمIPlayableفي مساحة الأسماءMedia. تمثّل هذه الواجهة العقد لأي شيء يمكنه تشغيل الوسائط، سواء كان هاتفًا أو تلفازًا أو مشغّل موسيقى مخصّصًا. يجب أن تعلن عن method واحدة هيPlay()تُرجع string.MediaFile.cs: عرّف فئة abstract باسمMediaFileفي مساحة الأسماءMedia. يمثّل هذا الأساس المشترك لجميع ملفات الوسائط. يجب أن يحتوي على الخاصيةTitle(من النوع string) والخاصيةDuration(من النوع int، وتمثّل عدد الثواني). أضف constructor يعيّن القيمتين كلتيهما. أضف method concrete باسمGetInfo()تُرجع"{Title} ({Duration}s)". كذلك أعلن عن method abstract باسمGetType()تُرجع string؛ إذ سيحدّد كل نوع من الوسائط نوع الملف الذي يمثّله.AudioFile.cs: عرّف فئةAudioFileفي مساحة الأسماءMedia، بحيث ترث منMediaFileوتطبّقIPlayable. أضف الخاصيةArtist(من النوع string). يجب أن يقبل constructor العنوان والمدة واسم الفنان. نفّذ override للـGetType()بحيث تُرجع"Audio". نفّذPlay()بحيث تُرجع"Playing audio: {Title} by {Artist}".Program.cs: في ملفك الرئيسي، أنشئAudioFileباستخدام قيم الإدخال. وضّح كلا جانبي الفئة: استدعِGetInfo()وGetType()(الموروثتين من الفئة abstract)، ثم استدعِPlay()(من interface). خزّن ملف الصوت في متغيّر من النوعIPlayableواستدعِPlay()مرة أخرى لإظهار تعدد الأشكال عبر interface.
ستتلقى ثلاث قيم إدخال:
- عنوان الأغنية
- المدة بالثواني
- اسم الفنان
اطبع الناتج بهذا التنسيق:
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()) من الفئة abstract، وفي الوقت نفسه تفي بعقد IPlayable. توفّر الفئة abstract إعادة استخدام التعليمات البرمجية لأنواع الوسائط المرتبطة، بينما تتيح interface لأي فئة غير مرتبطة أن تكون "playable" من دون مشاركة تسلسل هرمي للوراثة.
جرّب بنفسك
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
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات OOP
الملفات الخارجيةNamespaces والتوجيهاتمقدمة في Classes و Objectsالكلمة المفتاحية 'this'Methods والمعاملاتFields مقابل PropertiesConstructorsObject Initializersمراجعة - آلة حاسبة بسيطة4الوراثة
صيغة الوراثة الأساسية (:)الكلمة المفتاحية 'base'الكلمات المفتاحية Virtual و Overrideالأصناف المختومة (Sealed Classes)الفئة الأساسية 'object'مراجعة - هيكلية الموظفين7ميزات متقدمة
تحميل العوامل (Operator Overloading)المفهرسات (Indexers (this[]))إعادة تعريف ()ToStringدوال التوسعة (Extension Methods)ملخص - Custom List2الخصائص والأعضاء الساكنة (Static Members)
الخصائص المطبقة تلقائيًا (Auto-Implemented Properties)خصائص القراءة فقط والكتابة فقطالحقول والدوال الساكنة (Static)الفئات الساكنة (Static Classes)الأعضاء ذات البنية التعبيرية (Expression-Bodied Members)5تعدد الأشكال والواجهات (Polymorphism & Interfaces)
تعدد الأشكال: وقت التجميع مقابل وقت التشغيلالواجهة مقابل الفئة المجردة (Abstract Class)الواجهات المتعددةالواجهات الصريحةUpcasting و Downcastingملخص - حاسبة الأشكال8مفاهيم البرمجة كائنية التوجه (OOP) المتقدمة
التركيب بدلاً من الوراثةالـ Generics (الأصناف والدوال)الـ Delegates والأحداثالـ Attributes والـ Reflectionواجهة IDisposable وجملة usingأساسيات Dependency Injectionتدرّب بنفسك: مترجم C# عبر الإنترنت