نمط المحول
جزء من قسم البرمجة كائنية التوجه في رحلة Dart على Coddy. الدرس 95 من 110.
يسمح نمط Adapter للواجهات غير المتوافقة بالعمل معًا. ويعمل كجسر بين فئتين لا يمكنهما التعاون لولا ذلك، إذ يغلّف فئة موجودة بواجهة جديدة يتوقعها clients. ويكون هذا مفيدًا بشكل خاص عند دمج مكتبات تابعة لجهات خارجية أو تعليمات برمجية legacy في تطبيقك.
تخيّل أن لديك نظامًا يعمل مع بيانات JSON، لكنك تحتاج إلى دمج مكوّن لا يُخرج إلا XML. بدلًا من تعديل أيٍّ من class، تنشئ adapter يترجم بينهما:
// الواجهة الموجودة التي يتوقعها نظامك
abstract class JsonData {
String getJson();
}
// فئة قديمة بواجهة غير متوافقة
class XmlService {
String getXml() {
return '<data><name>John</name></data>';
}
}
// المحول يجعل XmlService متوافقًا مع JsonData
class XmlToJsonAdapter implements JsonData {
final XmlService _xmlService;
XmlToJsonAdapter(this._xmlService);
@override
String getJson() {
String xml = _xmlService.getXml();
// تحويل XML إلى JSON (مبسط)
return '{"name": "John"}';
}
}
void main() {
var xmlService = XmlService();
var adapter = XmlToJsonAdapter(xmlService);
print(adapter.getJson()); // {"name": "John"}
}يقوم XmlToJsonAdapter بتغليف XmlService وتنفيذ واجهة JsonData. يتعامل كود العميل مع واجهة JsonData المألوفة دون معرفة أن البيانات جاءت في الأصل من مصدر XML. يتولى المحوّل عملية الترجمة بأكملها داخليًا.
استخدم نمط Adapter عندما تريد استخدام فئة موجودة ولكن واجهتها لا تتطابق مع ما تحتاج إليه، أو عندما تحتاج إلى إنشاء فئة قابلة لإعادة الاستخدام تتعاون مع فئات ذات واجهات غير متوافقة.
التحدي
سهللنبنِ نظام adapter لمشغّل وسائط! تخيّل أن لديك نظام audio حديثًا يتوقّع من جميع مصادر الوسائط توفير الصوت من خلال interface قياسي، لكنك تحتاج إلى دمج مشغّل أسطوانات فينيل قديم يتبع طريقة مختلفة تمامًا لإنتاج الصوت.
ستنشئ ملفين لتنظيم تنفيذ نمط adapter لديك:
media_adapter.dart: يحتوي هذا الملف على interface وclassات adapter. عرّف classًا abstract باسمAudioSourceيتوقّعه نظامك الحديث — يجب أن يتضمن method باسمString playAudio()يعيد مخرجات audio كسلسلة نصية. ثم أنشئ class باسمVinylPlayerيمثّل المعدات legacy — إذ يحتوي على حقلtrackNameوmethod باسمString spinRecord()يعيد*crackle* Playing vinyl: [trackName] *crackle*. وأخيرًا، أنشئVinylAdapterالذي implementsAudioSourceويغلفVinylPlayer. يجب أن يستدعي methodplayAudio()الخاص بـ adapter methodspinRecord()الخاص بمشغّل الفينيل ويعيد result — وبذلك يربط بين الواجهتين غير المتوافقتين.main.dart: استورد ملف media adapter ووضّح كيف يجعل adapter مشغّل الفينيل القديم يعمل مع code يتوقّع interfaceAudioSourceالحديث. أنشئVinylPlayerباسم المسارBohemian Rhapsody. ثم أنشئVinylAdapterيغلف مشغّل الفينيل هذا. خزّن adapter في variable من النوعAudioSourceلإثبات توافقه مع interface المتوقّع. استدعِplayAudio()على مصدر audio الخاص بك واطبع result.
يكمن جمال هذا النمط في أن code الرئيسي لديك يعمل مع interface AudioSource القياسي — ولا يحتاج إلى معرفة أن مشغّل فينيل قديمًا ينفّذ العمل الفعلي خلف الكواليس!
المخرجات المتوقعة:
*crackle* Playing vinyl: Bohemian Rhapsody *crackle*جرّب بنفسك
import 'media_adapter.dart';
void main() {
// TODO: أنشئ VinylPlayer باسم المقطع "Bohemian Rhapsody"
// TODO: أنشئ VinylAdapter يغلّف مشغل الفينيل
// TODO: خزّن المحوّل في متغير من نوع AudioSource
// TODO: استدعِ playAudio() على مصدر الصوت واطبع النتيجة
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات الـ OOP
الملفات الخارجيةالمكتبات والـ Importsمقدمة في الـ OOPالـ Classes مقابل الـ Objectsالكلمة المفتاحية thisالـ Methodsمتغيرات الـ Instanceأساسيات الـ Constructorمراجعة - آلة حاسبة بسيطة4أمان الـ Null
مقدمة في أمان الـ NullNullable مقابل Non-Nullableعاملا الـ ? و !الكلمة المفتاحية Late وأمان الـ Nullالعوامل المدركة للـ Nullأمان الـ Null في الـ Classesمراجعة - نظام ملف تعريف المستخدم7الفئات المجردة والواجهات
الفئات المجردةالدوال المجردةالواجهات في Dartالواجهات الضمنيةالتنفيذ مقابل التوسيعالواجهات المتعددةمراجعة - حاسبة الأشكال10المجموعات والأنماط العامة
نظرة عامة على List و Set و Mapالمجموعات آمنة النوعالفئات العامةالدوال العامةقيود الأنماط العامةIterable و Iteratorملخص - التخزين العام13مفاهيم OOP المتقدمة
التركيب مقابل الوراثة (Composition vs Inheritance)دوال التوسعة (Extension Methods)الأصناف القابلة للاستدعاء (Callable Classes)الأصناف المختومة (Sealed Classes - Dart 3)السجلات (Records - Dart 3)الأنماط والمطابقة (Patterns & Matching - 3.0)التعدادات مع الدوال (Enums with Methods)2المنشئات في Dart
المنشئ الافتراضيالمنشئات المسماةقوائم التهيئةالمنشئات الثابتةالمنشئات المصنعيةالمنشئات الموجهةملخص - بناء الأشكال5التغليف (Encapsulation)
الأعضاء العامة مقابل الخاصةاتفاقية البادئة _الخصوصية على مستوى المكتبةتعمق في Getters و Settersإخفاء المعلوماتمراجعة - سجلات الطلاب8Mixins
مقدمة إلى Mixinsإنشاء Mixinsاستخدام Mixins متعددةالكلمة المفتاحية on في MixinsMixin مقابل الوراثة (Inheritance)Mixin مقابل الواجهة (Interface)مراجعة - نظام الحيوانات (Animal System)3خصائص الـ Class
أعضاء الـ Instance مقابل الـ Staticحقول Final و Constمتغيرات Lateالـ Static Methods والحقولدوال الـ Getters والـ Settersمراجعة - مدير الحساب البنكي6الوراثة
أساسيات الوراثةالكلمة المفتاحية superإعادة تعريف الدوال (Method Overriding)الوسم @overrideالكلمة المفتاحية final Classالدوال البانية والوراثةمراجعة - هيكلية الموظفين9تعدد الأشكال
أساسيات تعدد الأشكالتعدد الأشكال عبر الـ Interfacesالتحقق من النوع أثناء التشغيلالمعاملات is و asالكلمة المفتاحية Covariantملخص - معالج الدفع12البرمجة الكائنية غير المتزامنة (Async OOP)
Futures و async/awaitأساسيات الـ Streamsمتحكمات الـ Streamsالـ Constructors غير المتزامنةالعمليات غير المتزامنة في دوال الكلاسمراجعة - جالب البيانات (Data Fetcher)15أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودعتدرّب بنفسك: مترجم Dart عبر الإنترنت