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

نمط طريقة القالب

جزء من قسم Object Oriented Programming في رحلة Java على Coddy — الدرس 73 من 87.

يُعد نمط طريقة القالب (Template Method Pattern) نمط تصميم سلوكي يحدد الهيكل العام لخوارزمية في فئة أساسية (base class)، مما يسمح للفئات الفرعية (subclasses) بإعادة تعريف خطوات معينة دون تغيير الهيكل العام. فكر في الأمر كأنه وصفة طعام: تظل عملية الطهي العامة كما هي، ولكن يمكن أن تختلف المكونات أو التقنيات الفردية.

يستخدم النمط فئة مجردة مع طريقة قالب (template method) تستدعي تسلسلاً من الخطوات. تمتلك بعض الخطوات تطبيقات افتراضية، بينما تكون الخطوات الأخرى مجردة ويجب تنفيذها بواسطة الفئات الفرعية:

abstract class DataProcessor {
    // Template method - يحدد هيكل الخوارزمية
    public final void process() {
        readData();
        processData();
        saveData();
    }
    
    abstract void readData();
    abstract void processData();
    
    // التنفيذ الافتراضي (hook method)
    void saveData() {
        System.out.println("Saving to default location");
    }
}

class CSVProcessor extends DataProcessor {
    void readData() {
        System.out.println("Reading CSV file");
    }
    
    void processData() {
        System.out.println("Parsing CSV data");
    }
}

class XMLProcessor extends DataProcessor {
    void readData() {
        System.out.println("Reading XML file");
    }
    
    void processData() {
        System.out.println("Parsing XML data");
    }
}

عادةً ما يتم تمييز قالب الطريقة (template method) بكلمة final لمنع الفئات الفرعية من تغيير هيكل الخوارزمية. عند استدعائها، فإنها تنفذ نفس التسلسل لجميع الفئات الفرعية:

DataProcessor csv = new CSVProcessor();
csv.process();
// المخرجات:
// قراءة ملف CSV
// تحليل بيانات CSV
// الحفظ في الموقع الافتراضي

يعد نمط طريقة القالب (Template Method Pattern) مثاليًا عندما يكون لديك خوارزميات تشترك في نفس الهيكل ولكنها تختلف في خطوات محددة. فهو يعزز إعادة استخدام الكود من خلال وضع المنطق المشترك في الفئة الأساسية (base class) مع السماح بالتخصيص من خلال الوراثة (inheritance).

challenge icon

التحدي

سهل

لنقم ببناء نظام لإنشاء المستندات باستخدام نمط "Template Method Pattern"! ستقوم بإنشاء إطار عمل حيث تتبع أنواع المستندات المختلفة (التقارير والفواتير) نفس عملية الإنشاء ولكنها تخصص خطوات محددة. هذا مثالي للسيناريوهات التي يكون فيها سير العمل العام ثابتاً، ولكن التفاصيل تختلف حسب نوع المستند.

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

  • DocumentGenerator.java: قم بإنشاء الفئة الأساسية المجردة (abstract base class) التي تحدد طريقة القالب (template method). يجب أن يحتوي DocumentGenerator على:

    طريقة final تسمى generateDocument() تحدد هيكل الخوارزمية من خلال استدعاء ثلاث خطوات بالترتيب: createHeader()، و createBody()، و createFooter().

    طريقتان مجردتان (abstract methods): createHeader() و createBody() يجب على الفئات الفرعية تنفيذهما.

    طريقة خطاف (hook method) تسمى createFooter() مع تنفيذ افتراضي يطبع --- End of Document ---.

  • ReportGenerator.java: قم بإنشاء فئة فرعية ملموسة (concrete subclass) لإنشاء التقارير. يجب أن تأخذ عنوان التقرير (String) من خلال المنشئ (constructor) الخاص بها.

    طريقة createHeader() تطبع === REPORT: [title] ===

    طريقة createBody() تطبع Report content goes here...

    استخدم التذييل الافتراضي الموروث.

  • InvoiceGenerator.java: قم بإنشاء فئة فرعية ملموسة أخرى لإنشاء الفواتير. يجب أن تأخذ اسم العميل (String) والمبلغ (double) من خلال المنشئ الخاص بها.

    طريقة createHeader() تطبع *** INVOICE ***

    طريقة createBody() تطبع Customer: [name] في سطر واحد، ثم Amount Due: $[amount] في السطر التالي (قم بتنسيق المبلغ إلى منزلتين عشريتين).

    قم بإعادة تعريف (Override) لـ createFooter() لطباعة Thank you for your business!

  • Main.java: اجمع نظام طريقة القالب الخاص بك معاً! ستتلقى ثلاثة مدخلات: عنوان التقرير (String)، واسم العميل (String)، ومبلغ الفاتورة (double).

    قم بإنشاء ReportGenerator بالعنوان واستدعِ generateDocument(). اطبع سطراً فارغاً للفصل. ثم قم بإنشاء InvoiceGenerator باسم العميل والمبلغ، واستدعِ generateDocument().

ستتلقى ثلاثة مدخلات بالترتيب: عنوان التقرير (String)، واسم العميل (String)، ومبلغ الفاتورة (double).

على سبيل المثال، مع المدخلات Q4 Sales، و Acme Corp، و 1500.00، سيكون مخرجك هو:

=== REPORT: Q4 Sales ===
Report content goes here...
--- End of Document ---

*** INVOICE ***
Customer: Acme Corp
Amount Due: $1500.00
Thank you for your business!

لاحظ كيف يتبع كلا نوعي المستندات نفس العملية المكونة من ثلاث خطوات والمحددة في طريقة القالب، ولكن كل منهما يخصص الخطوات بشكل مختلف. يستخدم ReportGenerator التذييل الافتراضي بينما يوفر InvoiceGenerator تذييله الخاص — هذه هي قوة طرق الخطاف (hook methods) في نمط "Template Method Pattern"!

ورقة مرجعية

نمط Template Method Pattern هو نمط تصميم سلوكي يحدد الهيكل العظمي لخوارزمية في فئة أساسية، مما يسمح للفئات الفرعية بتجاوز خطوات محددة دون تغيير الهيكل العام.

يستخدم هذا النمط فئة مجردة (abstract class) تحتوي على template method تستدعي تسلسلاً من الخطوات. بعض الخطوات لها تطبيقات افتراضية، بينما البعض الآخر مجرد ويجب تنفيذه بواسطة الفئات الفرعية:

abstract class DataProcessor {
    // Template method - يحدد هيكل الخوارزمية
    public final void process() {
        readData();
        processData();
        saveData();
    }
    
    abstract void readData();
    abstract void processData();
    
    // تطبيق افتراضي (hook method)
    void saveData() {
        System.out.println("Saving to default location");
    }
}

تقوم الفئات الفرعية الملموسة بتنفيذ الأساليب المجردة:

class CSVProcessor extends DataProcessor {
    void readData() {
        System.out.println("Reading CSV file");
    }
    
    void processData() {
        System.out.println("Parsing CSV data");
    }
}

يتم تمييز الـ template method بكلمة final لمنع الفئات الفرعية من تغيير هيكل الخوارزمية. عند استدعائها، فإنها تنفذ نفس التسلسل لجميع الفئات الفرعية:

DataProcessor csv = new CSVProcessor();
csv.process();
// المخرجات:
// Reading CSV file
// Parsing CSV data
// Saving to default location

الخصائص الرئيسية:

  • الـ Template method تكون final وتحدد هيكل الخوارزمية
  • يجب تنفيذ الأساليب المجردة (Abstract methods) بواسطة الفئات الفرعية
  • توفر أساليب الخطاف (Hook methods) تطبيقات افتراضية يمكن تجاوزها اختيارياً
  • يعزز إعادة استخدام الكود من خلال وضع المنطق المشترك في الفئة الأساسية

جرّب بنفسك

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // قراءة المدخلات
        String reportTitle = scanner.nextLine();
        String customerName = scanner.nextLine();
        double invoiceAmount = scanner.nextDouble();
        
        // TODO: قم بإنشاء ReportGenerator بالعنوان واستدعاء generateDocument()
        
        // TODO: اطبع سطراً فارغاً للفصل
        
        // TODO: قم بإنشاء InvoiceGenerator باسم العميل والمبلغ، واستدعاء generateDocument()
        
    }
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming