نمط 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: ولا حاجة إلى إجراء تغييرات على التعليمات البرمجية الموجودة.
التحدي
سهللنَبْنِ نظامًا لتنسيق النصوص باستخدام نمط Strategy! ستنشئ نظامًا يمكن فيه تبديل استراتيجيات formatting المختلفة أثناء التشغيل لتحويل النص بطرق متنوعة، وهو مثالي لتطبيق يحتاج إلى دعم تنسيقات إخراج متعددة.
ستنظّم شيفرتك عبر أربعة ملفات:
TextFormatter.java: عرّف الواجهةTextFormatterالتي ستقوم جميع استراتيجياتformattingبتنفيذها. يجب أن تعلن عن أسلوب واحدString format(String text)يأخذ سلسلة نصية ويُرجع النسخةformatted.Formatters.java: أنشئ ثلاث استراتيجياتconcreteتنفّذ واجهةTextFormatterالخاصة بك:UpperCaseFormatter- يحوّل النص إلى أحرف كبيرة بالكامل.LowerCaseFormatter- يحوّل النص إلى أحرف صغيرة بالكامل.TitleCaseFormatter-capitalizesالحرف الأول من كل كلمة مع جعل بقية الأحرف صغيرة. تفصل المسافات بين الكلمات.TextEditor.java: أنشئ فئةcontextالتي تستخدم استراتيجيةformatting. يجب أن يحتويTextEditorالخاص بك على:حقل خاص للاحتفاظ باستراتيجية
TextFormattercurrent.أسلوب
setFormatter(TextFormatter formatter)يغيّر استراتيجيةformattingactive.أسلوب
publishText(String text)يطبّقformattercurrentعلى النص ويطبعPublished: [formatted text]. إذا لم يتم تعيين أيformatter، فيجب أن يطبعPublished: [original text]دون تغيير.Main.java: اجمع نظام الاستراتيجيات الخاص بك! ستتلقى مدخلين: النص المراد تنسيقه (String) ونمطformatting(String:"upper"أو"lower"أو"title").أنشئ
TextEditorواستدعِpublishTextأولًا باستخدام نص الإدخال قبل تعيين أيformatter(لتوضيح السلوك الافتراضي).بعد ذلك، وبناءً على إدخال النمط، عيّن استراتيجية
formatterappropriateواستدعِ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 مرة أخرى
}
}يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات الـ OOP
الملفات الخارجيةمقدمة في الـ OOPالفئات (Classes) مقابل الكائنات (Objects)الكلمة المفتاحية thisالدوال (Methods)الحقول (Attributes)دالة البناء (Constructor)تعدد أشكال دالة البناء (Constructor Overloading)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسية (extends)الكلمة المفتاحية superإعادة تعريف الدوال (@Override)تسلسل المنشئات (Constructor Chaining)كلاس Objectالوراثة الأحادية ومتعددة المستوياتلماذا لا توجد وراثة متعددة للكلاساتمراجعة - هيكلية الموظفين7الدوال الخاصة وفئة Object
دالة toString()equals() و hashCode()دالة clone()compareTo() و Comparableواجهة Comparatorمراجعة - الترتيب المخصص2محددات الوصول والتغليف
نظرة عامة على مستويات الوصولدوال Getter و Setterإخفاء البياناتالكلمة المفتاحية finalمراجعة - مدير الحساب البنكي5تعدد الأشكال (Polymorphism)
أساسيات الـ Method Overloadingالـ Method Overriding (في وقت التشغيل)الـ Upcasting والـ Downcastingمعامل الـ instanceofالفئات والدوال المجردة (Abstract)مراجعة - حاسبة الأشكال8مفاهيم OOP المتقدمة
الـ Composition مقابل الـ Inheritanceالـ Aggregation مقابل الـ Compositionالفئات الداخلية والمتداخلة والمجهولة (Inner, Nested & Anonymous)الـ Enums ودوالهاالـ Records (Java 16+)الـ Sealed Classes (Java 17+)11أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Builderنمط Observerنمط Strategy3خصائص الفئة والعضو الساكن
المتغيرات الخاصة بالكائن مقابل المتغيرات الساكنةالدوال الساكنةالكتل البرمجية الساكنةالثوابت (static final)ملخص - العداد والأدوات المساعدة6الواجهات (Interfaces) والأصناف المجردة (Abstract Classes)
مقدمة في الواجهات (Interfaces)تنفيذ الواجهات (Interfaces)تنفيذ واجهات متعددةالـ Default و Static في الواجهاتالأصناف المجردة مقابل الواجهاتالواجهات الوظيفية (Functional Interfaces)مراجعة - نظام الدفع9الأنواع العامة (Generics)
مقدمة في الأنواع العامةالأصناف العامة (Generic Classes)الدوال العامة (Generic Methods)معاملات الأنواع المقيدةالرموز البديلة (?, extends, super)مراجعة - الحاويات العامة12أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المكررتدرّب بنفسك: مترجم Java عبر الإنترنت