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

نمط Strategy

جزء من قسم البرمجة كائنية التوجه في رحلة Java على Coddy. الدرس 69 من 87.

يُعد Strategy Pattern نمط تصميم سلوكيًا يتيح لك تعريف عائلة من الخوارزميات، وتغليف كل خوارزمية في فئتها الخاصة، وجعلها قابلة للتبديل. وبدلًا من ترميز سلوك محدد بشكل ثابت، يمكنك تبديل الخوارزميات أثناء التشغيل دون تغيير الشيفرة التي تستخدمها.

تخيّل نظام دفع يدعم طرق دفع متعددة. من دون نمط الاستراتيجية، سينتهي بك الأمر إلى منطق شرطي فوضوي. وباستخدامه، تصبح كل طريقة دفع استراتيجية خاصة بها:

interface PaymentStrategy {
    void pay(int amount);
}

class CreditCardPayment implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " via Credit Card");
    }
}

class PayPalPayment implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " via PayPal");
    }
}

يحتفظ context class بمرجع إلى استراتيجية ويفوّض العمل إليها. النقطة الأساسية هي أن context لا يعرف أي استراتيجية concrete يستخدمها. إنه يعرف فقط interface:

class ShoppingCart {
    private PaymentStrategy paymentStrategy;
    
    public void setPaymentStrategy(PaymentStrategy strategy) {
        this.paymentStrategy = strategy;
    }
    
    public void checkout(int amount) {
        paymentStrategy.pay(amount);
    }
}

يمكنك الآن تغيير سلوك الدفع ديناميكيًا:

ShoppingCart cart = new ShoppingCart();

cart.setPaymentStrategy(new CreditCardPayment());
cart.checkout(100);  // تم دفع 100 عبر بطاقة الائتمان

cart.setPaymentStrategy(new PayPalPayment());
cart.checkout(50);   // تم دفع 50 عبر PayPal

يتألق نمط الاستراتيجية عندما تكون لديك طرق متعددة لتنفيذ إجراء ما وتريد التبديل بينها بسهولة. إن إضافة طريقة دفع جديدة تعني ببساطة إنشاء فئة جديدة تنفّذ PaymentStrategy: ولا حاجة إلى إجراء تغييرات على التعليمات البرمجية الموجودة.

challenge icon

التحدي

سهل

لنَبْنِ نظامًا لتنسيق النصوص باستخدام نمط Strategy! ستنشئ نظامًا يمكن فيه تبديل استراتيجيات formatting المختلفة أثناء التشغيل لتحويل النص بطرق متنوعة، وهو مثالي لتطبيق يحتاج إلى دعم تنسيقات إخراج متعددة.

ستنظّم شيفرتك عبر أربعة ملفات:

  • TextFormatter.java: عرّف الواجهة TextFormatter التي ستقوم جميع استراتيجيات formatting بتنفيذها. يجب أن تعلن عن أسلوب واحد String format(String text) يأخذ سلسلة نصية ويُرجع النسخة formatted.
  • Formatters.java: أنشئ ثلاث استراتيجيات concrete تنفّذ واجهة TextFormatter الخاصة بك:

    UpperCaseFormatter - يحوّل النص إلى أحرف كبيرة بالكامل.

    LowerCaseFormatter - يحوّل النص إلى أحرف صغيرة بالكامل.

    TitleCaseFormatter - capitalizes الحرف الأول من كل كلمة مع جعل بقية الأحرف صغيرة. تفصل المسافات بين الكلمات.

  • TextEditor.java: أنشئ فئة context التي تستخدم استراتيجية formatting. يجب أن يحتوي TextEditor الخاص بك على:

    حقل خاص للاحتفاظ باستراتيجية TextFormatter current.

    أسلوب setFormatter(TextFormatter formatter) يغيّر استراتيجية formatting active.

    أسلوب publishText(String text) يطبّق formatter current على النص ويطبع Published: [formatted text]. إذا لم يتم تعيين أي formatter، فيجب أن يطبع Published: [original text] دون تغيير.

  • Main.java: اجمع نظام الاستراتيجيات الخاص بك! ستتلقى مدخلين: النص المراد تنسيقه (String) ونمط formatting (String: "upper" أو "lower" أو "title").

    أنشئ TextEditor واستدعِ publishText أولًا باستخدام نص الإدخال قبل تعيين أي formatter (لتوضيح السلوك الافتراضي).

    بعد ذلك، وبناءً على إدخال النمط، عيّن استراتيجية formatter appropriate واستدعِ publishText مرة أخرى باستخدام النص نفسه لإظهار النتيجة formatted.

ستتلقى مدخلين بالترتيب: النص المراد تنسيقه (String) ونمط formatting (String).

على سبيل المثال، عند استخدام المدخلين hello world وtitle، سيكون الإخراج كما يلي:

Published: hello world
Published: Hello World

لاحظ أن TextEditor لا يعرف أي شيء عن منطق formatting المحدد. فهو يوكّل المهمة ببساطة إلى الاستراتيجية المعيّنة حاليًا. وهذا يجعل إضافة أنماط formatting جديدة أمرًا سهلًا للغاية دون تعديل شيفرة المحرر!

جرّب بنفسك

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String text = scanner.nextLine();
        String style = scanner.nextLine();
        
        // TODO: إنشاء كائن من TextEditor
        
        // TODO: استدعاء publishText مع النص المدخل (قبل تعيين أي formatter)
        
        // TODO: بناءً على النمط المدخل ("upper" أو "lower" أو "title")،
        // قم بتعيين الـ formatter المناسب واستدعاء publishText مرة أخرى
        
    }
}
quiz iconاختبر نفسك

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

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم Java عبر الإنترنت