يعتبر نموذج الإستراتيجية ( نمط الاستراتيجية ) (Strategy Pattern) أو (خطة العمل) ( The policy pattern) واحداً من أنماط تصميم البرمجيات التصرفية (السلوكية) في مجال هندسة البرمجيات التي وضعها جماعة الأربعة في كتابهم المعروف (نماذج التصميم).[1][2][3] يستعمل هذا النموذج (النمط) بالتحديد كي يتم اختيار الخوارزمية المناسبة أثناء تشغيل البرنامج (runtime ). بدلا من تنفيذ خوارزمية واحدة مباشرة، الكود يستقبل التعليمات اثناء التشغيل (run-time) ليحدد الخوارزمية المناسبة لاستخدامها. بعبارة أخرى، فإن هذا النموذج يعرّف عددا من الخوارزميات ويجعلهم مغلفين (encapsulated) بحيث يمكن أن تحل إحداها محل الأخرى.
على سبيل المثال، يمكن تطبيق نموذج الإستراتيجية في حالة صنف (Class) يقوم بعملية التحقق من صحة البيانات المدخلة (validation ). حيث يمكن أن يكتب البرنامج بطريقة تجعله يختار الخوارزمية المناسبة تلقائيا بناءً على نوع البيانات المدخلة أو على مصدر هذه البيانات أو على أي عامل آخر. ما يهم هنا هو أن هذا العامل لا تتم معرفته، وبالتالي تحديد الخوارزمية، إلا أثناء تشغيل البرنامج. يمكن استخدام خوارزميات التحقق (الاستراتيجيات) ، المغلفة (encapsulated) بشكل منفصل عن كائن التحقق (validating object) ، من قبل كائنات التحقق الأخرى في مجالات مختلفة من النظام (أو حتى أنظمة مختلفة) دون تكرار الكود البرمجي (code duplication). عادة يقوم نمط الإستراتيجية بتخزين عنوان مرجعي (reference) لبعض الكود في بنية بيانات (data structure) ويستردها (retrieves ). يمكن تحقيق ذلك من خلال آليات مثل مؤشر الدالة الأصلية (native function pointer) ، أو دالة من الصنف الأول (first-class function) ، أو الاصناف أو مثيلات الصنف (class instances) في لغات البرمجة الكائنية (object-oriented) ، أو الوصول (accessing )إلى التخزين الداخلي (internal storage) لتطبيق اللغة (the language implementation) الخاص بالكود عبر الانعكاس (reflection).
هيكل
مخطط الصنف في لغةالنمذجة الموحدة (UML) ومخطط التتابع
.في مخطط الصنف UML (UML class diagram) أعلاه، لا يطبق صنف السياق (Context class) خوارزمية مباشرة. بدلاً من ذلك، يشير السياقContext إلى واجهة الإستراتيجية Strategy interface لتنفيذ خوارزمية (()Strategy.algorithm ) ، مما يجعل السياقContext مستقلاً عن كيفية تطبيق (implemented) الخوارزمية. صنفا الإستراتيجيةStrategy1 ( Strategy1) والاستراتيجية 2 ( Strategy2) ينفذا (implement) واجهة الإستراتيجية ( Strategy interface)، أي تنفيذ (تغليف) ( implement (encapsulate)) خوارزمية. يعرض مخطط التتابع UML (UML sequence diagram) تفاعلات وقت التشغيل ( run-time interactions) : يقوم كائن السياق (Context object) بتفويض (delegates) خوارزمية لكائنات إستراتيجية (Strategy objects) مختلفة. أولاً، يستدعيContext السياق خوارزمية ()algorithm على كائن Strategy1 ، الذي ينفذ الخوارزمية ويعيدreturns النتيجة إلى السياقContext. بعد ذلك، يغير السياقContext استراتيجيته ويستدعي ()algorithm على كائن Strategy2 ، الذي ينفذ الخوارزمية ويعيد (returns) النتيجة إلى السياق(Context).
مخطط الفئة
مثال
#C
المثال التالي بلغة برمجة سي شارب (#C):
public class StrategyPatternWiki { public static void Main(String[] args) { // Prepare strategies var normalStrategy = new NormalStrategy(); var happyHourStrategy = new HappyHourStrategy(); var firstCustomer = new Customer(normalStrategy); // Normal billing firstCustomer.Add(1.0, 1); // Start Happy Hour firstCustomer.Strategy = happyHourStrategy; firstCustomer.Add(1.0, 2); // New Customer Customer secondCustomer = new Customer(happyHourStrategy); secondCustomer.Add(0.8, 1); // The Customer pays firstCustomer.PrintBill(); // End Happy Hour secondCustomer.Strategy = normalStrategy; secondCustomer.Add(1.3, 2); secondCustomer.Add(2.5, 1); secondCustomer.PrintBill(); } } class Customer { private IList<double> drinks; // Get/Set Strategy public IBillingStrategy Strategy { get; set; } public Customer(IBillingStrategy strategy) { this.drinks = new List<double>(); this.Strategy = strategy; } public void Add(double price, int quantity) { this.drinks.Add(this.Strategy.GetActPrice(price * quantity)); } // Payment of bill public void PrintBill() { double sum = 0; foreach (var drinkCost in this.drinks) { sum += drinkCost; } Console.WriteLine($"Total due: {sum}."); this.drinks.Clear(); } } interface IBillingStrategy { double GetActPrice(double rawPrice); } // Normal billing strategy (unchanged price) class NormalStrategy : IBillingStrategy { public double GetActPrice(double rawPrice) => rawPrice; } // Strategy for Happy hour (50% discount) class HappyHourStrategy : IBillingStrategy { public double GetActPrice(double rawPrice) => rawPrice * 0.5; }
جافا Java
مثال بلغة برمجة جافا Java
import java.util.ArrayList; interface BillingStrategy { // Use a price in cents to avoid floating point round-off error int getActPrice(int rawPrice); // Normal billing strategy (unchanged price) static BillingStrategy normalStrategy() { return rawPrice -> rawPrice; } // Strategy for Happy hour (50% discount) static BillingStrategy happyHourStrategy() { return rawPrice -> rawPrice / 2; } } class Customer { private final List<Integer> drinks = new ArrayList<>(); private BillingStrategy strategy; public Customer(BillingStrategy strategy) { this.strategy = strategy; } public void add(int price, int quantity) { this.drinks.add(this.strategy.getActPrice(price*quantity)); } // Payment of bill public void printBill() { int sum = this.drinks.stream().mapToInt(v -> v).sum(); System.out.println("Total due: " + sum); this.drinks.clear(); } // Set Strategy public void setStrategy(BillingStrategy strategy) { this.strategy = strategy; } } public class StrategyPattern { public static void main(String[] arguments) { // Prepare strategies BillingStrategy normalStrategy = BillingStrategy.normalStrategy(); BillingStrategy happyHourStrategy = BillingStrategy.happyHourStrategy(); Customer firstCustomer = new Customer(normalStrategy); // Normal billing firstCustomer.add(100, 1); // Start Happy Hour firstCustomer.setStrategy(happyHourStrategy); firstCustomer.add(100, 2); // New Customer Customer secondCustomer = new Customer(happyHourStrategy); secondCustomer.add(80, 1); // The Customer pays firstCustomer.printBill(); // End Happy Hour secondCustomer.setStrategy(normalStrategy); secondCustomer.add(130, 2); secondCustomer.add(250, 1); secondCustomer.printBill(); } }
استراتيجية ومبدأ مفتوح/مغلق
استراتيجية و مبدأ مفتوح مغلق (Strategy and open/closed principle) وفقًا لنمط الإستراتيجية ، لا ينبغي أن تُتوارث (inherited ) سلوكيات الصنف (the behaviors of a class) . بدلاً من ذلك ، يجب تغليفها ( encapsulated) باستخدام الواجهات (interfaces) . هذا متوافق مع مبدأ مفتوح / مغلق (OCP)، الذي يقترح أن الأصناف (classes) يجب أن تكون مفتوحة للتمديد (open for extension) ولكن مغلقة للتعديل (closed for modification).
كمثال ، ضع في اعتبارك صنف السيارة(car class). وظيفتان محتملتان للسيارة (functionalities ) هما الفرملة (brake) و التسارع (accelerate). نظرًا لأن سلوكيات التسارع (accelerate ) والفرملة (brake ) تتغير بشكل متكرر (frequently) بين النماذج (models)، فإن النهج المشترك (common approach) هو تنفيذ (implement ) هذه السلوكيات (behaviors ) في الأصنف الفرعية (subclasses). هذا النهج (approach ) له عيوب كبيرة (significant drawbacks): يجب الإعلان (declared ) عن سلوكيات التسارع (accelerate )والفرامل (brake) في كل طراز سيارة جديد (new Car model). يزداد عمل إدارة (work of managing) هذه السلوكيات (behaviors ) بشكل كبير مع زيادة عدد النماذج (as the number of models increases) ، ويتطلب تكرار الكود (duplicated )عبر النماذج (models). بالإضافة إلى ذلك، ليس من السهل تحديد الطبيعة الدقيقة ( exact nature) للسلوك (behavior ) لكل نموذج (model ) دون التحقق من الكود في كل نموذج.
يستخدم نمط الاستراتيجية (strategy pattern) التركيب بدلاً من الوراثة (composition over inheritance). في نمط الاستراتيجية، يتم تعريف السلوكيات (behaviors are defined) على أنها واجهات منفصلة (interfaces ) و أصناف محددة (specific classes) تنفذ (implement )هذه الواجهات (interfaces). هذا يسمح بفصل (better decoupling) أفضل بين السلوك (behavior ) والصنف (class ) الذي يستخدم السلوك (). يمكن تغيير السلوك (behavior ) دون كسر الاصناف (breaking the classes) التي تستخدمه ، ويمكن للأصناف (classes ) التبديل بين السلوكيات (switch between behaviors) عن طريق تغيير التنفيذ المحدد (by changing the specific implementation) المستخدم دون الحاجة إلى أي تغييرات كبيرة في الكود (Code). يمكن أيضًا تغيير السلوكيات (Behaviors ) في وقت التشغيل (run-time) وكذلك في وقت التصميم (design-time). على سبيل المثال ، يمكن تغيير سلوك فرامل كائن السيارة (car object's brake behavior) من دالة الفرملة مع ABS (()BrakeWithABS) إلى دالة الفرملة (()Brake ) عن طريق تغيير عضو سلوك الفرامل brakeBehavior ( by changing the brakeBehavior member ) إلى:
brakeBehavior = new Brake();
مثال بلغة جافا Java
/* Encapsulated family of Algorithms * Interface and its implementations */ public interface IBrakeBehavior { public void brake(); } public class BrakeWithABS implements IBrakeBehavior { public void brake() { System.out.println("Brake with ABS applied"); } } public class Brake implements IBrakeBehavior { public void brake() { System.out.println("Simple Brake applied"); } } /* Client that can use the algorithms above interchangeably */ public abstract class Car { private IBrakeBehavior brakeBehavior; public Car(IBrakeBehavior brakeBehavior) { this.brakeBehavior = brakeBehavior; } public void applyBrake() { brakeBehavior.brake(); } public void setBrakeBehavior(IBrakeBehavior brakeType) { this.brakeBehavior = brakeType; } } /* Client 1 uses one algorithm (Brake) in the constructor */ public class Sedan extends Car { public Sedan() { super(new Brake()); } } /* Client 2 uses another algorithm (BrakeWithABS) in the constructor */ public class SUV extends Car { public SUV() { super(new BrakeWithABS()); } } /* Using the Car example */ public class CarExample { public static void main(final String[] arguments) { Car sedanCar = new Sedan(); sedanCar.applyBrake(); // This will invoke class "Brake" Car suvCar = new SUV(); suvCar.applyBrake(); // This will invoke class "BrakeWithABS" // set brake behavior dynamically suvCar.setBrakeBehavior( new Brake() ); suvCar.applyBrake(); // This will invoke class "Brake" } }
مراجع
- "The Strategy design pattern - Structure and Collaboration". w3sDesign.com. مؤرشف من الأصل في 24 أغسطس 201712 أغسطس 2017.
- "The Strategy design pattern - Problem, Solution, and Applicability". w3sDesign.com. مؤرشف من الأصل في 13 أغسطس 201712 أغسطس 2017.
- Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley. صفحات 315ff. . مؤرشف من الأصل في 15 ديسمبر 2019.
- "The Strategy design pattern - Structure and Collaboration". w3sDesign.com. مؤرشف من الأصل في 24 أغسطس 201712 أغسطس 2017.
- http://www.mcdonaldland.info/2007/11/28/40/ - تصفح: نسخة محفوظة 2020-04-14 على موقع واي باك مشين.