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

نمط المحول

جزء من قسم Object Oriented Programming في رحلة Python على Coddy — الدرس 50 من 64.

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

إليك نظامان بواجهات غير متوافقة:

class OldPrinter:
    def old_print(self, text):
        return f"OLD: {text}"

class NewPrinter:
    def print(self, text):
        return f"NEW: {text}"

تستخدم الطابعة القديمة old_print() بينما تستخدم الطابعة الجديدة print().

قم بإنشاء محول (adapter) لجعل الطابعة القديمة تعمل مع الواجهة الجديدة:

class PrinterAdapter:
    def __init__(self, old_printer):
        self.old_printer = old_printer
    
    def print(self, text):
        # تكييف الواجهة القديمة مع الواجهة الجديدة
        return self.old_printer.old_print(text)

يقوم المحول بتغليف الطابعة القديمة ويوفر الواجهة المتوقعة.

استخدم كلا الطابعتين بنفس كود العميل:

def print_document(printer, text):
    return printer.print(text)  # يتوقع استدعاء الدالة print()

# استخدام الطابعة الجديدة مباشرة
new_printer = NewPrinter()
print(print_document(new_printer, "Hello"))

# استخدام الطابعة القديمة من خلال المحول (adapter)
old_printer = OldPrinter()
adapter = PrinterAdapter(old_printer)
print(print_document(adapter, "Hello"))

أنشئ مثالاً آخر مع مشغلات الوسائط:

class Mp3Player:
    def play_mp3(self, filename):
        return f"Playing MP3: {filename}"

class Mp4Player:
    def play_mp4(self, filename):
        return f"Playing MP4: {filename}"

class MediaAdapter:
    def __init__(self, player, audio_type):
        self.player = player
        self.audio_type = audio_type
    
    def play(self, filename):
        if self.audio_type == "mp3":
            return self.player.play_mp3(filename)
        elif self.audio_type == "mp4":
            return self.player.play_mp4(filename)
class AudioPlayer:
    def play(self, audio_type, filename):
        if audio_type == "mp3":
            return Mp3Player().play_mp3(filename)
        else:
            adapter = MediaAdapter(Mp4Player(), audio_type)
            return adapter.play(filename)

player = AudioPlayer()
print(player.play("mp3", "song.mp3"))
print(player.play("mp4", "video.mp4"))

المخرجات:

NEW: Hello
OLD: Hello
Playing MP3: song.mp3
Playing MP4: video.mp4

نقطة رئيسية: يجعل نمط المحول (Adapter Pattern) الواجهات غير المتوافقة تعمل معاً من خلال تغليف فئة (class) موجودة بواجهة جديدة. يقوم المحول بترجمة الاستدعاءات من الواجهة المتوقعة إلى الواجهة الفعلية للكائن المغلف. هذا مفيد لدمج الكود القديم (legacy code) أو مكتبات الطرف الثالث دون تعديل الكود الموجود.

challenge icon

التحدي

متوسط

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

 

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

تحتاج إلى:

  1. فهم الواجهات الحديثة المحددة في interfaces.py (لا يمكن تعديل هذا الملف)
  2. فحص المكونات القديمة في legacy_system.py (لا يمكن تعديل هذا الملف)
  3. تنفيذ فئات المحول (adapter classes) في adapters.py التي تجعل المكونات القديمة متوافقة مع الواجهات الحديثة
  4. إنشاء نظام حديث في modern_system.py يستخدم هذه الواجهات

ورقة مرجعية

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

هيكل المحول الأساسي:

class PrinterAdapter:
    def __init__(self, old_printer):
        self.old_printer = old_printer
    
    def print(self, text):
        # تكييف الواجهة القديمة مع الواجهة الجديدة
        return self.old_printer.old_print(text)

مثال مع واجهات طابعة غير متوافقة:

class OldPrinter:
    def old_print(self, text):
        return f"OLD: {text}"

class NewPrinter:
    def print(self, text):
        return f"NEW: {text}"

# كود العميل يتوقع طريقة print()
def print_document(printer, text):
    return printer.print(text)

# استخدام الطابعة القديمة من خلال المحول
old_printer = OldPrinter()
adapter = PrinterAdapter(old_printer)
print(print_document(adapter, "Hello"))  # OLD: Hello

مثال لمحول مشغل الوسائط:

class MediaAdapter:
    def __init__(self, player, audio_type):
        self.player = player
        self.audio_type = audio_type
    
    def play(self, filename):
        if self.audio_type == "mp3":
            return self.player.play_mp3(filename)
        elif self.audio_type == "mp4":
            return self.player.play_mp4(filename)

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

جرّب بنفسك

# استيراد جميع الفئات اللازمة
from adapters import LegacyDataAnalyzerAdapter, LegacyChartGeneratorAdapter
from modern_system import AnalyticsSystem

# معالج حالات الاختبار الشاملة
test_case = input()

if test_case == "basic_adapter_test":
    # اختبار وظائف المحول الأساسية
    processor = LegacyDataAnalyzerAdapter()
    data = [1, 2, 3, 4, 5]
    processor.process_data(data)
    results = processor.get_results()
    print(f"Results: {results}")

elif test_case == "visualizer_adapter_test":
    # اختبار محول المصور البياني
    visualizer = LegacyChartGeneratorAdapter("bar")
    data = [10, 20, 30, 40, 50]
    success = visualizer.visualize(data)
    if success:
        export_success = visualizer.export_visualization("test_chart.png")
        print(f"Visualization exported: {export_success}")

elif test_case == "analytics_system_test":
    # اختبار نظام التحليل الكامل
    processor = LegacyDataAnalyzerAdapter()
    visualizer = LegacyChartGeneratorAdapter("line")
    system = AnalyticsSystem(processor, visualizer)
    
    data = [15, 25, 35, 45, 55]
    result = system.analyze_and_visualize(data, "analytics_output.png")
    print(f"System result: {result}")

elif test_case == "validation_error_test":
    # اختبار أخطاء التحقق من الصحة
    processor = LegacyDataAnalyzerAdapter()
    try:
        processor.process_data("invalid_data")
        print("Validation failed - should have raised ValueError")
    except ValueError as e:
        print(f"Validation error caught: {e}")

elif test_case == "empty_data_test":
    # اختبار مع بيانات فارغة
    processor = LegacyDataAnalyzerAdapter()
    try:
        processor.process_data([])
        results = processor.get_results()
        print(f"Empty data results: {results}")
    except Exception as e:
        print(f"Empty data error: {e}")

elif test_case == "chart_types_test":
    # اختبار أنواع مختلفة من المخططات البيانية
    chart_types = ["bar", "line", "pie"]
    data = [5, 15, 25, 35]
    
    for chart_type in chart_types:
        visualizer = LegacyChartGeneratorAdapter(chart_type)
        success = visualizer.visualize(data)
        print(f"{chart_type} chart created: {success}")

elif test_case == "file_extension_test":
    # اختبار التعامل مع امتدادات الملفات
    visualizer = LegacyChartGeneratorAdapter()
    data = [1, 2, 3, 4]
    visualizer.visualize(data)
    
    # اختبار تنسيقات أسماء ملفات متنوعة
    filenames = ["test", "test.jpg", "test.pdf", "test.png"]
    for filename in filenames:
        success = visualizer.export_visualization(filename)
        print(f"Export '{filename}': {success}")

elif test_case == "summary_test":
    # اختبار ملخص التحليل
    processor = LegacyDataAnalyzerAdapter()
    visualizer = LegacyChartGeneratorAdapter()
    system = AnalyticsSystem(processor, visualizer)
    
    data = [10, 20, 30, 40, 50, 60]
    system.analyze_and_visualize(data)
    summary = system.get_analysis_summary()
    print("Analysis Summary:")
    print(summary)

elif test_case == "mixed_data_test":
    # اختبار مع بيانات عددية مختلطة
    processor = LegacyDataAnalyzerAdapter()
    data = [1.5, 2, 3.7, 4, 5.2]
    processor.process_data(data)
    results = processor.get_results()
    print(f"Mixed data results: {results}")

elif test_case == "invalid_numeric_test":
    # اختبار مع بيانات عددية غير صالحة
    processor = LegacyDataAnalyzerAdapter()
    try:
        processor.process_data([1, 2, "three", 4, 5])
        print("Validation failed - should have raised ValueError")
    except ValueError as e:
        print(f"Invalid numeric data error: {e}")

elif test_case == "no_results_summary_test":
    # اختبار الملخص بدون نتائج
    processor = LegacyDataAnalyzerAdapter()
    visualizer = LegacyChartGeneratorAdapter()
    system = AnalyticsSystem(processor, visualizer)
    
    summary = system.get_analysis_summary()
    print(f"No results summary: {summary}")

elif test_case == "large_dataset_test":
    # اختبار مع مجموعة بيانات كبيرة
    processor = LegacyDataAnalyzerAdapter()
    data = list(range(1, 101))  # من 1 إلى 100
    processor.process_data(data)
    results = processor.get_results()
    print(f"Large dataset - Mean: {results.get('mean', 'N/A'):.2f}")
    print(f"Large dataset - Min: {results.get('min', 'N/A')}")
    print(f"Large dataset - Max: {results.get('max', 'N/A')}")

elif test_case == "complete_workflow_test":
    # اختبار سير العمل الكامل مع عمليات متعددة
    processor = LegacyDataAnalyzerAdapter()
    bar_visualizer = LegacyChartGeneratorAdapter("bar")
    line_visualizer = LegacyChartGeneratorAdapter("line")
    
    data = [12, 18, 25, 32, 28, 35, 42]
    
    # إنشاء أنظمة تحليل مع مصورات بيانية مختلفة
    bar_system = AnalyticsSystem(processor, bar_visualizer)
    line_system = AnalyticsSystem(LegacyDataAnalyzerAdapter(), line_visualizer)
    
    # تشغيل التحليلات
    bar_result = bar_system.analyze_and_visualize(data, "bar_chart.png")
    line_result = line_system.analyze_and_visualize(data, "line_chart.png")
    
    print("Complete workflow test completed")
    print(f"Bar chart result: {bar_result['visualization_file']}")
    print(f"Line chart result: {line_result['visualization_file']}")
    
    # الحصول على الملخصات
    print("Bar system summary:")
    print(bar_system.get_analysis_summary())
    print("Line system summary:")
    print(line_system.get_analysis_summary())
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming