نمط طريقة القالب
جزء من قسم Object Oriented Programming في رحلة Dart على Coddy — الدرس 97 من 110.
يحدد نمط طريقة القالب (Template Method pattern) الهيكل العام لخوارزمية في فئة أساسية، مما يسمح للفئات الفرعية بإعادة تعريف خطوات معينة دون تغيير الهيكل العام. تتحكم الفئة الأساسية في سير العمل، بينما تقوم الفئات الفرعية بتخصيص الخطوات الفردية.
يستخدم هذا النمط الوراثة: حيث تُعرّف فئة مجردة (abstract class) "طريقة قالب" (template method) تستدعي سلسلة من الخطوات، بعضها يكون مجرداً (يجب تنفيذه بواسطة الفئات الفرعية) وبعضها الآخر له تنفيذات افتراضية:
abstract class DataProcessor {
// Template method - يحدد هيكل الخوارزمية
void process() {
readData();
processData();
saveData();
}
void readData(); // Abstract - يجب على الفئات الفرعية تنفيذها
void processData(); // Abstract - يجب على الفئات الفرعية تنفيذها
void saveData() { // تنفيذ افتراضي
print('Data saved to database');
}
}
class CsvProcessor extends DataProcessor {
@override
void readData() => print('Reading CSV file');
@override
void processData() => print('Parsing CSV rows');
}
class JsonProcessor extends DataProcessor {
@override
void readData() => print('Reading JSON file');
@override
void processData() => print('Parsing JSON objects');
@override
void saveData() => print('Saved to cloud storage');
}
void main() {
var csv = CsvProcessor();
csv.process();
// Reading CSV file
// Parsing CSV rows
// Data saved to database
var json = JsonProcessor();
json.process();
// Reading JSON file
// Parsing JSON objects
// Saved to cloud storage
}تُعد الطريقة process() هي القالب - فهي تحدد ترتيب العمليات. تقوم الفئات الفرعية بتنفيذ readData() و processData() بشكل مختلف، ويمكنها اختيارياً تجاوز saveData(). الفائدة الرئيسية هي أن هيكل الخوارزمية يظل ثابتاً عبر جميع التنفيذات مع السماح بالمرونة في الخطوات الفردية.
التحدي
سهللنقم ببناء نظام لإنشاء التقارير باستخدام نمط Template Method! ستقوم بإنشاء إطار عمل تتبع فيه أنواع التقارير المختلفة نفس الهيكل العام، ولكن يقوم كل نوع تقرير بتخصيص خطوات محددة مثل جمع البيانات وتنسيق المخرجات.
ستقوم بتنظيم الكود الخاص بك في ملفين:
report_generator.dart: يحتوي هذا الملف على الفئة الأساسية المجردة (abstract base class) وتطبيقات التقارير الملموسة (concrete implementations). قم بإنشاء فئة مجردة باسمReportGeneratorمع تابع قالب (template method) يسمىgenerate()يحدد سير عمل إنشاء التقرير بهذا الترتيب:fetchData()، وanalyzeData()، وformatReport()، وdeliver(). يجب أن تكون التوابعfetchData()وformatReport()مجردة (حيث يتعامل كل نوع تقرير معها بشكل مختلف)، بينما يجب أن يطبعanalyzeData()النصAnalyzing data...ويطبعdeliver()النصReport delivered via email.كتطبيقات افتراضية. ثم قم بإنشاء فئتين ملموستين للتقارير:SalesReport- يطبع التابعfetchData()الخاص بهاFetching sales data from database...ويطبع التابعformatReport()الخاص بهاFormatting as bar charts and tables.InventoryReport- يطبع التابعfetchData()الخاص بهاFetching inventory levels from warehouse...، ويطبع التابعformatReport()الخاص بهاFormatting as inventory list.، وتقوم بإعادة تعريف (override) التابعdeliver()ليطبعReport sent to warehouse manager.
main.dart: قم باستيراد ملف منشئ التقارير واستعرض كيف يحافظ نمط Template Method على اتساق هيكل الخوارزمية مع السماح بالتخصيص. قم بإنشاء كائن منSalesReportواستدعِ تابعgenerate()الخاص به. ثم اطبع سطرًا فارغًا للفصل. قم بإنشاء كائن منInventoryReportواستدعِ تابعgenerate()الخاص به أيضًا.
لاحظ كيف يتبع كلا التقريرين نفس العملية المكونة من أربع خطوات، ولكن كل منهما يخصص الخطوات التي تهم غرضه المحدد. حتى أن InventoryReport يقوم بإعادة تعريف طريقة التسليم الافتراضية لإرسال التقارير إلى مستلم مختلف!
المخرجات المتوقعة:
Fetching sales data from database...
Analyzing data...
Formatting as bar charts and tables.
Report delivered via email.
Fetching inventory levels from warehouse...
Analyzing data...
Formatting as inventory list.
Report sent to warehouse manager.ورقة مرجعية
يحدد نمط طريقة القالب (Template Method pattern) الهيكل العظمي لخوارزمية في فئة أساسية، مما يسمح للفئات الفرعية بتجاوز خطوات محددة دون تغيير الهيكل العام.
تُعرف الفئة المجردة (abstract class) "طريقة قالب" تستدعي تسلسلاً من الخطوات. بعض الخطوات تكون مجردة (يجب تنفيذها بواسطة الفئات الفرعية) وبعضها يحتوي على تنفيذات افتراضية:
abstract class DataProcessor {
// طريقة القالب - تحدد هيكل الخوارزمية
void process() {
readData();
processData();
saveData();
}
void readData(); // مجردة - يجب على الفئات الفرعية تنفيذها
void processData(); // مجردة - يجب على الفئات الفرعية تنفيذها
void saveData() { // تنفيذ افتراضي
print('Data saved to database');
}
}
class CsvProcessor extends DataProcessor {
@override
void readData() => print('Reading CSV file');
@override
void processData() => print('Parsing CSV rows');
}
class JsonProcessor extends DataProcessor {
@override
void readData() => print('Reading JSON file');
@override
void processData() => print('Parsing JSON objects');
@override
void saveData() => print('Saved to cloud storage');
}
تتحكم "طريقة القالب" في ترتيب سير العمل. تقوم الفئات الفرعية بتخصيص الخطوات الفردية من خلال تنفيذ الطرق المجردة وتجاوز التنفيذات الافتراضية اختيارياً. يظل هيكل الخوارزمية ثابتاً عبر جميع التنفيذات.
جرّب بنفسك
import 'report_generator.dart';
void main() {
// TODO: قم بإنشاء SalesReport واستدعاء التابع generate() الخاص به
// TODO: اطبع سطرًا فارغًا للفصل
// TODO: قم بإنشاء InventoryReport واستدعاء التابع generate() الخاص به
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
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أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع