الواجهات الصريحة
جزء من قسم Object Oriented Programming في رحلة C# على Coddy — الدرس 28 من 70.
عندما تقوم فئة (class) بتنفيذ واجهات (interfaces) متعددة تحتوي على أعضاء بنفس الاسم، فإنك تواجه تعارضاً في التسمية. يحل التنفيذ الصريح للواجهة (Explicit interface implementation) هذه المشكلة من خلال السماح لك بتقديم تنفيذات منفصلة لكل واجهة.
مع التنفيذ الصريح، تقوم بوضع اسم الواجهة كبادئة لاسم العضو وتحذف محدد الوصول:
public interface IAmericanPlug
{
void Connect();
}
public interface IEuropeanPlug
{
void Connect();
}
public class UniversalAdapter : IAmericanPlug, IEuropeanPlug
{
void IAmericanPlug.Connect()
{
Console.WriteLine("Connected to 120V");
}
void IEuropeanPlug.Connect()
{
Console.WriteLine("Connected to 220V");
}
}يمكن الوصول إلى الأعضاء المنفذين بشكل صريح فقط من خلال متغير من نوع الواجهة (interface)، وليس من خلال الفئة (class) نفسها:
var adapter = new UniversalAdapter();
// adapter.Connect(); // لن يتم التجميع!
IAmericanPlug american = adapter;
american.Connect(); // متصل بـ 120 فولت
IEuropeanPlug european = adapter;
european.Connect(); // متصل بـ 220 فولتتعد هذه التقنية مفيدة أيضاً عندما تريد إخفاء أعضاء الواجهة (interface members) من واجهة برمجة التطبيقات (API) العامة للفئة. فإذا كانت الطريقة (method) منطقية فقط عند التعامل مع الكائن كواجهة محددة، فإن التنفيذ الصريح (explicit implementation) يحافظ على نظافة مساحة سطح الفئة الخاصة بك مع الاستمرار في الوفاء بالعقد (contract).
التحدي
سهللنقم ببناء نظام منشئ تقارير يوضح كيف يسمح التنفيذ الصريح للواجهة (explicit interface implementation) لفئة واحدة بتقديم سلوكيات مختلفة اعتماداً على الواجهة المستخدمة. ستقوم بإنشاء مستند يمكن تنسيقه بشكل مختلف للعرض مقابل الطباعة.
ستقوم بتنظيم الكود الخاص بك عبر أربعة ملفات:
IDisplayable.cs: قم بتعريف واجهة تسمىIDisplayableداخل مساحة الأسماءReports. تمثل هذه الواجهة المحتوى الذي يمكن عرضه على الشاشة. يجب أن تصرح عن طريقةRender()التي تعيد سلسلة نصية (string).IPrintable.cs: قم بتعريف واجهة تسمىIPrintableداخل مساحة الأسماءReports. تمثل هذه الواجهة المحتوى الذي يمكن إرساله إلى الطابعة. يجب أن تصرح أيضاً عن طريقةRender()تعيد سلسلة نصية - لاحظ أن هذا يؤدي إلى تصادم في الأسماء معIDisplayable!Report.cs: قم بتعريف فئةReportفي مساحة الأسماءReportsالتي تنفذ كلاً منIDisplayableوIPrintable. يجب أن يحتوي التقرير على خاصيةTitle(string) وخاصيةContent(string)، مع مشيد (constructor) يضبط كلتا القيمتين. استخدم التنفيذ الصريح للواجهة لتوفير سلوكياتRender()مختلفة:- عند الرندر للعرض:
"[SCREEN] {Title}: {Content}" - عند الرندر للطباعة:
"[PRINT] {Title} | {Content}"
- عند الرندر للعرض:
Program.cs: في ملفك الرئيسي، قم بإنشاءReportباستخدام قيم الإدخال. نظراً لأنه لا يمكن الوصول إلى التنفيذات الصريحة إلا من خلال متغيرات الواجهة، قم بتخزين نفس التقرير في كل من متغيرIDisplayableومتغيرIPrintable، ثم استدعِRender()على كل منهما لإظهار كيف ينتج نفس الكائن مخرجات مختلفة بناءً على الواجهة التي تستخدمها.
ستتلقى مدخلين:
- عنوان التقرير (Report title)
- محتوى التقرير (Report content)
اطبع المخرجات بهذا التنسيق:
Display version:
{Render() via IDisplayable}
Print version:
{Render() via IPrintable}على سبيل المثال، إذا كانت المدخلات هي Sales Report و Q4 revenue increased by 15%، يجب أن تكون المخرجات:
Display version:
[SCREEN] Sales Report: Q4 revenue increased by 15%
Print version:
[PRINT] Sales Report | Q4 revenue increased by 15%لاحظ كيف يتصرف نفس كائن Report بشكل مختلف اعتماداً على مرجع الواجهة الذي تستخدمه للوصول إليه. هذه هي قوة التنفيذ الصريح للواجهة - حل تصادم الأسماء مع توفير سلوك مناسب للسياق!
ورقة مرجعية
عندما تقوم فئة (class) بتنفيذ واجهات (interfaces) متعددة تحتوي على أعضاء بنفس الاسم، يمكنك استخدام التنفيذ الصريح للواجهة (explicit interface implementation) لتوفير تنفيذات منفصلة لكل واجهة.
لتنفيذ عضو واجهة بشكل صريح، قم ببادئة اسم العضو باسم الواجهة واحذف معدل الوصول (access modifier):
public interface IAmericanPlug
{
void Connect();
}
public interface IEuropeanPlug
{
void Connect();
}
public class UniversalAdapter : IAmericanPlug, IEuropeanPlug
{
void IAmericanPlug.Connect()
{
Console.WriteLine("Connected to 120V");
}
void IEuropeanPlug.Connect()
{
Console.WriteLine("Connected to 220V");
}
}لا يمكن الوصول إلى الأعضاء المنفذة بشكل صريح إلا من خلال متغير من نوع الواجهة، وليس من خلال الفئة نفسها:
var adapter = new UniversalAdapter();
// adapter.Connect(); // لن يتم التجميع!
IAmericanPlug american = adapter;
american.Connect(); // Connected to 120V
IEuropeanPlug european = adapter;
european.Connect(); // Connected to 220Vهذه التقنية مفيدة لـ:
- حل تعارض الأسماء بين أعضاء الواجهة
- إخفاء أعضاء الواجهة من واجهة برمجة التطبيقات العامة (public API) للفئة عندما لا يكون لها معنى إلا في سياق واجهة محددة
جرّب بنفسك
using System;
using Reports;
class Program
{
public static void Main(string[] args)
{
// قراءة المدخلات
string title = Console.ReadLine();
string content = Console.ReadLine();
// TODO: إنشاء كائن Report بالعنوان والمحتوى
// TODO: تخزين التقرير في متغير IDisplayable
// TODO: تخزين نفس التقرير في متغير IPrintable
// TODO: طباعة نسخة العرض عن طريق استدعاء Render() على IDisplayable
Console.WriteLine("Display version:");
// اطبع نتيجة العرض هنا
// TODO: طباعة نسخة الطباعة عن طريق استدعاء Render() على IPrintable
Console.WriteLine("Print version:");
// اطبع نتيجة العرض هنا
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
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