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

نمط المحول

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

يُعد نمط المحول (Adapter pattern) نمطاً هيكلياً يسمح للواجهات غير المتوافقة بالعمل معاً. فهو يعمل كجسر بين فئتين، حيث يقوم بتحويل واجهة فئة معينة إلى واجهة يتوقعها العملاء. فكر فيه كمحول طاقة يتيح لك توصيل جهاز أوروبي بمقبس أمريكي.

هذا النمط مفيد بشكل خاص عندما تريد استخدام فئة (class) موجودة ولكن واجهتها (interface) لا تتوافق مع ما يحتاجه الكود الخاص بك. بدلاً من تعديل الفئة الأصلية (التي قد لا تملك السيطرة عليها)، تقوم بإنشاء محول (adapter) يغلفها:

// فئة موجودة بواجهة غير متوافقة
public class OldPrinter
{
    public void PrintMessage(string msg) => Console.WriteLine($"Old: {msg}");
}

// الواجهة التي يتوقعها الكود الخاص بك
public interface IPrinter
{
    void Print(string text);
}

// المحول (Adapter) يسد الفجوة
public class PrinterAdapter : IPrinter
{
    private OldPrinter _oldPrinter;
    
    public PrinterAdapter(OldPrinter oldPrinter)
    {
        _oldPrinter = oldPrinter;
    }
    
    public void Print(string text)
    {
        _oldPrinter.PrintMessage(text);  // تفويض إلى الواجهة القديمة
    }
}

الآن يمكن للكود الخاص بك العمل مع الطابعة القديمة من خلال الواجهة المتوقعة:

OldPrinter legacy = new OldPrinter();
IPrinter printer = new PrinterAdapter(legacy);

printer.Print("Hello");  // القديم: Hello

يُعد نمط المحول (Adapter pattern) ذا قيمة لا تُقدر بثمن عند دمج مكتبات الطرف الثالث أو الكود القديم في تطبيقك. يعمل كود العميل مع واجهة IPrinter، وهو غير مدرك تماماً بأن فئة قديمة وغير متوافقة هي التي تقوم بالعمل الفعلي خلف الكواليس.

challenge icon

التحدي

سهل

لنقم ببناء نظام مشغل وسائط باستخدام نمط المحول (Adapter pattern). لديك مكتبة صوتية موجودة بالفعل تقوم بتشغيل ملفات MP3، ولكن تطبيقك يحتاج إلى واجهة موحدة يمكنها التعامل مع أنواع وسائط مختلفة. ستقوم بإنشاء محول لسد هذه الفجوة، مما يسمح لمشغل الوسائط الحديث الخاص بك بالعمل بسلاسة مع المكتبة الصوتية القديمة.

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

  • LegacyAudio.cs: يمثل هذا مكتبة موجودة لا يمكنك تعديلها. قم بإنشاء فئة LegacyAudioPlayer داخل مساحة الأسماء MediaSystem مع طريقة PlayMp3(string filename) التي تعيد السلسلة النصية "Playing MP3: {filename}". هذا هو "المُكيف عليه" (adaptee) - الفئة ذات الواجهة غير المتوافقة.
  • MediaAdapter.cs: قم بإنشاء الواجهة والمحول في نفس مساحة الأسماء. حدد واجهة IMediaPlayer مع طريقة Play(string filename) التي تعيد سلسلة نصية. ثم قم بإنشاء فئة MediaPlayerAdapter التي تنفذ IMediaPlayer. يجب أن يقوم المحول الخاص بك بتغليف مثيل من LegacyAudioPlayer (يتم تمريره عبر المنشئ) وترجمة استدعاء Play إلى طريقة PlayMp3 الخاصة بالمشغل القديم.
  • Program.cs: اجمع كل شيء معاً عن طريق إنشاء المشغل القديم، وتغليفه في المحول الخاص بك، واستخدام المحول من خلال واجهة IMediaPlayer. يوضح هذا كيف يمكن لكود العميل العمل مع الواجهة الحديثة بينما يتولى المحول الاتصال بالنظام القديم خلف الكواليس.

ستتلقى مدخلاً واحداً:

  • اسم الملف المراد تشغيله (على سبيل المثال، song.mp3)

قم بإنشاء LegacyAudioPlayer، وقم بتغليفه في MediaPlayerAdapter، ثم استدعِ Play على المحول باستخدام واجهة IMediaPlayer واطبع النتيجة.

على سبيل المثال، إذا كان الإدخال هو favorite_track.mp3، يجب أن يكون الإخراج:

Playing MP3: favorite_track.mp3

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

ورقة مرجعية

يُعد نمط المحول (Adapter pattern) نمطاً هيكلياً يسمح للواجهات غير المتوافقة بالعمل معاً من خلال العمل كجسر بين فئتين.

يقوم النمط بتحويل واجهة فئة معينة إلى واجهة يتوقعها العملاء، وهو مفيد عندما تريد استخدام فئة موجودة ولكن واجهتها لا تتطابق مع احتياجات الكود الخاص بك.

الهيكل الأساسي:

// فئة موجودة بواجهة غير متوافقة (Adaptee)
public class OldPrinter
{
    public void PrintMessage(string msg) => Console.WriteLine($"Old: {msg}");
}

// الواجهة المستهدفة التي يتوقعها الكود الخاص بك
public interface IPrinter
{
    void Print(string text);
}

// المحول يسد الفجوة
public class PrinterAdapter : IPrinter
{
    private OldPrinter _oldPrinter;
    
    public PrinterAdapter(OldPrinter oldPrinter)
    {
        _oldPrinter = oldPrinter;
    }
    
    public void Print(string text)
    {
        _oldPrinter.PrintMessage(text);  // يفوض العمل للواجهة القديمة
    }
}

الاستخدام:

OldPrinter legacy = new OldPrinter();
IPrinter printer = new PrinterAdapter(legacy);

printer.Print("Hello");  // Old: Hello

يعمل كود العميل مع الواجهة المستهدفة، دون أن يدرك أن فئة غير متوافقة هي التي تقوم بالعمل الفعلي خلف الكواليس. هذا النمط ذو قيمة خاصة عند دمج مكتبات خارجية أو أكواد قديمة (legacy code).

جرّب بنفسك

using System;
using MediaSystem;

class Program
{
    public static void Main(string[] args)
    {
        // قراءة مدخل اسم الملف
        string filename = Console.ReadLine();
        
        // TODO: إنشاء مثيل من LegacyAudioPlayer
        
        // TODO: تغليفه في MediaPlayerAdapter
        
        // TODO: استخدام المحول من خلال واجهة IMediaPlayer
        // وطباعة نتيجة استدعاء Play مع filename
    }
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming