نمط طريقة القالب
جزء من قسم Object Oriented Programming في رحلة C# على Coddy — الدرس 59 من 70.
إن نمط طريقة القالب (Template Method pattern) هو نمط سلوكي يحدد الهيكل العام لخوارزمية في فئة أساسية، مما يسمح للفئات الفرعية بتجاوز خطوات محددة دون تغيير الهيكل العام. تتحكم الفئة الأساسية في سير العمل، بينما تقوم الفئات المشتقة بتخصيص الأجزاء الفردية.
يستخدم هذا النمط الوراثة والأساليب المجردة. تحتوي الفئة الأساسية على "طريقة قالب" (template method) تستدعي سلسلة من الخطوات، بعضها مجرد ويجب تنفيذها بواسطة الفئات الفرعية:
public abstract class DataProcessor
{
// Template method - يحدد هيكل الخوارزمية
public void Process()
{
ReadData();
ProcessData();
SaveData();
}
protected abstract void ReadData();
protected abstract void ProcessData();
protected virtual void SaveData() => Console.WriteLine("Saving to default location");
}
public class CsvProcessor : DataProcessor
{
protected override void ReadData() => Console.WriteLine("Reading CSV file");
protected override void ProcessData() => Console.WriteLine("Parsing CSV data");
}
public class JsonProcessor : DataProcessor
{
protected override void ReadData() => Console.WriteLine("Reading JSON file");
protected override void ProcessData() => Console.WriteLine("Parsing JSON data");
protected override void SaveData() => Console.WriteLine("Saving to cloud");
}توفر كل فئة فرعية تنفيذها الخاص بينما تفرض الفئة الأساسية ترتيب العمليات:
DataProcessor csv = new CsvProcessor();
csv.Process();
// قراءة ملف CSV
// تحليل بيانات CSV
// الحفظ في الموقع الافتراضي
DataProcessor json = new JsonProcessor();
json.Process();
// قراءة ملف JSON
// تحليل بيانات JSON
// الحفظ في السحابةيعد نمط "طريقة القالب" (Template Method pattern) مثاليًا عندما تشترك فئات متعددة في نفس هيكل الخوارزمية ولكنها تختلف في خطوات محددة. فهو يعزز إعادة استخدام الكود من خلال وضع المنطق المشترك في الفئة الأساسية مع السماح بالمرونة من خلال الدوال القابلة للتجاوز.
التحدي
سهللنقم ببناء نظام لإنشاء التقارير باستخدام نمط Template Method. تشترك الأنواع المختلفة من التقارير (مثل تقارير المبيعات وتقارير المخزون) في نفس الهيكل العام - فجميعها بحاجة إلى جمع البيانات، وتنسيقها، وإخراج النتيجة - ولكن كل نوع من التقارير يتعامل مع هذه الخطوات بشكل مختلف.
ستقوم بتنظيم الكود الخاص بك عبر ثلاثة ملفات:
ReportGenerator.cs: أنشئ فئة مجردة (abstract class) باسمReportGeneratorداخل مساحة الأسماءReporting. تحدد هذه الفئة الأساسية الهيكل العظمي لخوارزمية إنشاء التقرير من خلال "طريقة قالب" (template method) تسمىGenerate(). يجب أن تستدعي هذه الطريقة ثلاث خطوات بالترتيب:GatherData()، وFormatReport()، وOutputReport(). اجعلGatherData()وFormatReport()مجردتين (ستقوم كل فئة تقرير بتنفيذهما بشكل مختلف)، بينما يجب أن تكونOutputReport()طريقة افتراضية (virtual method) مع تنفيذ افتراضي يطبعSending to default printer.ConcreteReports.cs: أنشئ فئتين محددتين للتقارير في نفس مساحة الأسماء ترثان منReportGenerator:SalesReport- تنفذGatherData()لطباعةCollecting sales data from databaseوFormatReport()لطباعةFormatting as sales summaryInventoryReport- تنفذGatherData()لطباعةScanning warehouse inventory، وFormatReport()لطباعةFormatting as inventory list، وتقوم بعمل override لـOutputReport()لطباعةSending to warehouse manager
Program.cs: اجمع كل شيء معاً عن طريق إنشاء مثيلات للتقارير وتوليدها. تضمن "طريقة القالب" (template method) أن يتبع كل تقرير نفس العملية المكونة من ثلاث خطوات، ولكن السلوك المحدد لكل خطوة يختلف بناءً على نوع التقرير.
ستتلقى مدخلاً واحداً:
- نوع التقرير:
salesأوinventory
أنشئ مولد التقارير المناسب بناءً على المدخلات واستدعِ طريقة Generate() الخاصة به.
على سبيل المثال، إذا كان المدخل هو sales، يجب أن يكون المخرج:
Collecting sales data from database
Formatting as sales summary
Sending to default printerإذا كان المدخل هو inventory، يجب أن يكون المخرج:
Scanning warehouse inventory
Formatting as inventory list
Sending to warehouse managerلاحظ كيف يتبع كلا التقريرين نفس هيكل الخوارزمية تماماً (الجمع، التنسيق، الإخراج)، ولكن تنفيذ كل خطوة مخصص. يستخدم SalesReport سلوك الإخراج الافتراضي، بينما يقوم InventoryReport بتجاوزه (override) - مما يوضح كيف يتيح لك نمط Template Method التحكم في الخطوات الإلزامية للتجاوز والخطوات التي يمكن تخصيصها اختيارياً!
جرّب بنفسك
using System;
using Reporting;
class Program
{
public static void Main(string[] args)
{
string reportType = Console.ReadLine();
// TODO: قم بإنشاء منشئ التقارير المناسب بناءً على reportType
// إذا كان reportType هو "sales"، فقم بإنشاء SalesReport
// إذا كان reportType هو "inventory"، فقم بإنشاء InventoryReport
// ثم قم باستدعاء الدالة Generate() في التقرير
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس 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