---
title: "الاستراتيجية"
type: "design-pattern"
slug: "strategy"
url: "http://localhost:3000/ar/design-patterns/strategy.md"
category: "الأنماط السلوكية"
description: "الاستراتيجية هو نمط تصميم سلوكي يتيح لك تعريف عائلة من الخوارزميات، ووضع كل منها في فئة منفصلة، وجعل كائناتها قابلة للتبادل."
languages: ["java", "csharp", "cpp", "go", "php", "python", "ruby", "rust", "swift", "typescript"]
---
# الاستراتيجية

> الاستراتيجية هو نمط تصميم سلوكي يتيح لك تعريف عائلة من الخوارزميات، ووضع كل منها في فئة منفصلة، وجعل كائناتها قابلة للتبادل.

## Intent

**الاستراتيجية** هو نمط تصميم سلوكي يتيح لك تعريف عائلة من الخوارزميات، ووضع كل منها في فئة منفصلة، وجعل كائناتها قابلة للتبادل.

## Problem

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

كانت أكثر الميزات المطلوبة في التطبيق هي التخطيط التلقائي للمسارات. يجب أن يتمكن المستخدم من إدخال عنوان ورؤية أسرع مسار إلى تلك الوجهة معروضاً على الخريطة.

كان الإصدار الأول من التطبيق قادراً فقط على بناء المسارات عبر الطرق. كان الناس الذين يسافرون بالسيارة يشعرون بالبهجة. لكن على ما يبدو، لا يحب الجميع القيادة في إجازتهم. لذا مع التحديث التالي، أضفت خياراً لبناء مسارات المشاة. وبعدها مباشرةً، أضفت خياراً آخر يتيح للناس استخدام وسائل النقل العام في مساراتهم.

غير أن ذلك كان مجرد البداية. خططت لاحقاً لإضافة بناء مسارات لراكبي الدراجات. وحتى في وقت لاحق، خياراً آخر لبناء مسارات عبر جميع المعالم السياحية في المدينة.

أصبح كود تطبيق الملاحة مفرطاً في الحجم.

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

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

علاوة على ذلك، أصبح العمل الجماعي غير فعّال. يشكو زملاؤك، الذين تم تعيينهم بعد الإصدار الناجح مباشرةً، من أنهم يقضون وقتاً طويلاً جداً في حل تعارضات الدمج. يتطلب تطبيق ميزة جديدة تغيير نفس الفئة الضخمة، مما يتعارض مع الكود الذي ينتجه الآخرون.

## Solution

يقترح نمط الاستراتيجية أن تأخذ فئة تفعل شيئاً محدداً بطرق كثيرة مختلفة وتستخرج جميع هذه الخوارزميات إلى فئات منفصلة تُسمى _الاستراتيجيات_.

يجب أن تحتوي الفئة الأصلية، المسماة _السياق_، على حقل لتخزين مرجع إلى إحدى الاستراتيجيات. يفوّض السياق العمل إلى كائن استراتيجية مرتبط بدلاً من تنفيذه بنفسه.

السياق ليس مسؤولاً عن اختيار الخوارزمية المناسبة للمهمة. بدلاً من ذلك، يُمرر العميل الاستراتيجية المطلوبة إلى السياق. في الواقع، لا يعرف السياق الكثير عن الاستراتيجيات. يعمل مع جميع الاستراتيجيات من خلال نفس الواجهة العامة، التي تعرض أسلوباً واحداً فقط لتشغيل الخوارزمية المغلّفة داخل الاستراتيجية المختارة.

بهذه الطريقة يصبح السياق مستقلاً عن الاستراتيجيات المحددة، مما يتيح لك إضافة خوارزميات جديدة أو تعديل الموجودة دون تغيير كود السياق أو الاستراتيجيات الأخرى.

استراتيجيات تخطيط المسارات.

في تطبيق الملاحة الخاص بنا، يمكن استخراج كل خوارزمية توجيه إلى فئتها الخاصة مع أسلوب `buildRoute` واحد. يقبل الأسلوب نقطة أصل ووجهة ويعيد مجموعة من نقاط تفتيش المسار.

على الرغم من أنه بنفس الوسيطات، قد تبني كل فئة توجيه مساراً مختلفاً، فإن الفئة الرئيسية لتطبيق الملاحة لا تهتم حقاً بالخوارزمية المختارة إذ إن مهمتها الأساسية هي عرض مجموعة من نقاط التفتيش على الخريطة. تمتلك الفئة أسلوباً للتبديل بين استراتيجيات التوجيه النشطة، لذا يمكن لعملائها، كأزرار واجهة المستخدم، استبدال سلوك التوجيه المحدد حالياً بآخر.

## Structure

1. يحتفظ **السياق** بمرجع إلى إحدى الاستراتيجيات المحددة ويتواصل مع هذا الكائن فقط عبر واجهة الاستراتيجية.
2. واجهة **الاستراتيجية** مشتركة بين جميع الاستراتيجيات المحددة. تُعلن عن أسلوب يستخدمه السياق لتنفيذ استراتيجية.
3. **الاستراتيجيات المحددة** تُنفّذ متغيرات مختلفة من الخوارزمية التي يستخدمها السياق.
4. يستدعي السياق أسلوب التنفيذ على كائن الاستراتيجية المرتبط في كل مرة يحتاج فيها إلى تشغيل الخوارزمية. لا يعرف السياق نوع الاستراتيجية التي يعمل معها أو كيفية تنفيذ الخوارزمية.
5. يُنشئ **العميل** كائن استراتيجية محدداً ويمرره إلى السياق. يُعرض السياق دالة ضبط تتيح للعملاء استبدال الاستراتيجية المرتبطة بالسياق أثناء وقت التشغيل.

## Pseudocode

في هذا المثال، يستخدم السياق **استراتيجيات** متعددة لتنفيذ عمليات حسابية متنوعة.

// تُعلن واجهة الاستراتيجية عن العمليات المشتركة لجميع
// الإصدارات المدعومة من خوارزمية ما. يستخدم السياق هذه
// الواجهة لاستدعاء الخوارزمية المُعرَّفة بواسطة
// الاستراتيجيات المحددة.
interface Strategy is
    method execute(a, b)

// تُنفّذ الاستراتيجيات المحددة الخوارزمية مع الالتزام
// بواجهة الاستراتيجية الأساسية. تجعلها الواجهة قابلة
// للتبادل في السياق.
class ConcreteStrategyAdd implements Strategy is
    method execute(a, b) is
        return a + b

class ConcreteStrategySubtract implements Strategy is
    method execute(a, b) is
        return a - b

class ConcreteStrategyMultiply implements Strategy is
    method execute(a, b) is
        return a * b

// يُحدد السياق الواجهة التي تهم العملاء.
class Context is
    // يحتفظ السياق بمرجع إلى أحد كائنات الاستراتيجية.
    // لا يعرف السياق الفئة المحددة للاستراتيجية. يجب أن يعمل
    // مع جميع الاستراتيجيات عبر واجهة الاستراتيجية.
    private strategy: Strategy

    // عادةً يقبل السياق استراتيجية عبر المُنشئ، ويوفر أيضاً
    // دالة ضبط لتمكين التبديل بين الاستراتيجيات أثناء وقت التشغيل.
    method setStrategy(Strategy strategy) is
        this.strategy = strategy

    // يفوّض السياق بعض العمل إلى كائن الاستراتيجية
    // بدلاً من تنفيذ نسخ متعددة من الخوارزمية بنفسه.
    method executeStrategy(int a, int b) is
        return strategy.execute(a, b)

// يختار كود العميل استراتيجية محددة ويمررها إلى السياق.
// يجب أن يكون العميل على دراية بالفروق بين الاستراتيجيات
// لاتخاذ الاختيار الصحيح.
class ExampleApplication is
    method main() is
        إنشاء كائن السياق.

        قراءة الرقم الأول.
        قراءة الرقم الأخير.
        قراءة الإجراء المطلوب من إدخال المستخدم.

        if (action == addition) then
            context.setStrategy(new ConcreteStrategyAdd())

        if (action == subtraction) then
            context.setStrategy(new ConcreteStrategySubtract())

        if (action == multiplication) then
            context.setStrategy(new ConcreteStrategyMultiply())

        result = context.executeStrategy(الرقم الأول, الرقم الثاني)

        طباعة النتيجة.

## Applicability

استخدم نمط الاستراتيجية عندما تريد استخدام متغيرات مختلفة من خوارزمية داخل كائن والقدرة على التبديل من خوارزمية إلى أخرى أثناء وقت التشغيل.

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

 استخدم الاستراتيجية عندما يكون لديك الكثير من الفئات المتشابهة التي تختلف فقط في طريقة تنفيذها لبعض السلوك.

 يتيح لك نمط الاستراتيجية استخراج السلوك المتغير إلى تسلسل هرمي منفصل للفئات ودمج الفئات الأصلية في فئة واحدة، مما يقلل من تكرار الكود.

 استخدم النمط لعزل منطق الأعمال لفئة ما عن تفاصيل تنفيذ الخوارزميات التي قد لا تكون بهذه الأهمية في سياق ذلك المنطق.

 يتيح لك نمط الاستراتيجية عزل الكود والبيانات الداخلية والتبعيات لخوارزميات مختلفة عن بقية الكود. يحصل العملاء المختلفون على واجهة بسيطة لتنفيذ الخوارزميات والتبديل بينها أثناء وقت التشغيل.

 استخدم النمط عندما تحتوي فئتك على جملة شرطية ضخمة تتبدل بين متغيرات مختلفة من نفس الخوارزمية.

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

## How to Implement

1. في فئة السياق، حدِّد خوارزمية عرضة للتغيير المتكرر. قد تكون أيضاً جملة شرطية ضخمة تختار وتُنفّذ متغيراً من نفس الخوارزمية أثناء وقت التشغيل.
2. أعلن عن واجهة الاستراتيجية المشتركة لجميع متغيرات الخوارزمية.
3. استخرج جميع الخوارزميات واحدةً تلو الأخرى إلى فئاتها الخاصة. يجب أن تُنفّذ جميعها واجهة الاستراتيجية.
4. في فئة السياق، أضف حقلاً لتخزين مرجع إلى كائن استراتيجية. وفّر دالة ضبط لاستبدال قيم ذلك الحقل. يجب أن يعمل السياق مع كائن الاستراتيجية فقط عبر واجهة الاستراتيجية. يجوز للسياق تعريف واجهة تُتيح للاستراتيجية الوصول إلى بياناته.
5. يجب على عملاء السياق ربطه باستراتيجية مناسبة تتطابق مع الطريقة التي يتوقعون بها من السياق أداء مهمته الأساسية.

## Pros

* يمكنك تبادل الخوارزميات المستخدمة داخل كائن ما أثناء وقت التشغيل.
* يمكنك عزل تفاصيل تنفيذ الخوارزمية عن الكود الذي يستخدمها.
* يمكنك استبدال الوراثة بالتركيب.
* _مبدأ الفتح/الإغلاق_. يمكنك إدخال استراتيجيات جديدة دون الحاجة إلى تغيير السياق.

## Cons

* إذا كان لديك خوارزميتان فقط ونادراً ما تتغيران، فلا يوجد سبب حقيقي لتعقيد البرنامج بفئات وواجهات جديدة تأتي مع النمط.
* يجب أن يكون العملاء على دراية بالفروق بين الاستراتيجيات لتمكينهم من اختيار الاستراتيجية المناسبة.
* تدعم كثير من لغات البرمجة الحديثة الأنواع الوظيفية (functional types) مما يتيح لك تنفيذ إصدارات مختلفة من خوارزمية داخل مجموعة من الدوال المجهولة. يمكنك حينها استخدام هذه الدوال بالضبط كما كنت ستستخدم كائنات الاستراتيجية، لكن دون إثقال كودك بفئات وواجهات إضافية.

## Relations with Other Patterns

* تمتلك أنماط [Bridge](/ar/design-patterns/bridge) و [State](/ar/design-patterns/state) و [Strategy](/ar/design-patterns/strategy) (وبدرجة ما [Adapter](/ar/design-patterns/adapter)) هياكل متشابهة جداً. في الواقع، تعتمد جميع هذه الأنماط على التركيب، أي تفويض العمل إلى كائنات أخرى. غير أنها تحل مشاكل مختلفة. النمط ليس مجرد وصفة لهيكلة كودك بطريقة معينة. يمكنه أيضاً إيصال للمطورين الآخرين المشكلة التي يحلها النمط.
* قد يبدو [Command](/ar/design-patterns/command) و [Strategy](/ar/design-patterns/strategy) متشابهَيْن لأنه يمكنك استخدام كليهما لتحديد معامل كائن بإجراء ما. غير أن لديهما نوايا مختلفة جداً.

  * يمكنك استخدام _Command_ لتحويل أي عملية إلى كائن. تصبح معاملات العملية حقولاً لذلك الكائن. يتيح لك التحويل تأجيل تنفيذ العملية، ووضعها في قائمة انتظار، وتخزين سجل الأوامر، وإرسال الأوامر إلى خدمات بعيدة، وما إلى ذلك.
  * من ناحية أخرى، يصف _Strategy_ عادةً طرقاً مختلفة لفعل الشيء ذاته، مما يتيح لك تبادل هذه الخوارزميات داخل فئة سياق واحدة.
* يتيح لك [Decorator](/ar/design-patterns/decorator) تغيير مظهر الكائن، بينما يتيح لك [Strategy](/ar/design-patterns/strategy) تغيير جوهره.
* يعتمد [Template Method](/ar/design-patterns/template-method) على الوراثة: يتيح لك تغيير أجزاء من خوارزمية عن طريق توسيع تلك الأجزاء في الفئات الفرعية. يعتمد [Strategy](/ar/design-patterns/strategy) على التركيب: يمكنك تغيير أجزاء من سلوك الكائن عن طريق تزويده باستراتيجيات مختلفة تتوافق مع ذلك السلوك. يعمل _Template Method_ على مستوى الفئة، لذا فهو ثابت. يعمل _Strategy_ على مستوى الكائن، مما يتيح لك التبديل بين السلوكيات أثناء وقت التشغيل.
* يمكن اعتبار [State](/ar/design-patterns/state) امتداداً لـ [Strategy](/ar/design-patterns/strategy). يعتمد كلا النمطين على التركيب: يغيران سلوك السياق عن طريق تفويض بعض العمل إلى كائنات مساعدة. يجعل _Strategy_ هذه الكائنات مستقلة تماماً وغير مدركة لبعضها. غير أن _State_ لا يُقيّد التبعيات بين الحالات المحددة، مما يتيح لها تغيير حالة السياق حسب الرغبة.
## Relations

**Related patterns**

- [الجسر](/ar/design-patterns/bridge.md)
- [الحالة](/ar/design-patterns/state.md)
- [المهايئ (Adapter)](/ar/design-patterns/adapter.md)
- [الأمر (Command)](/ar/design-patterns/command.md)
- [المُزَخرِف](/ar/design-patterns/decorator.md)
- [أسلوب القالب](/ar/design-patterns/template-method.md)

## Code Examples

### java

```java
package refactoring_guru.strategy.example.strategies;

/**
 * الواجهة المشتركة لجميع الاستراتيجيات.
 */
public interface PayStrategy {
    boolean pay(int paymentAmount);
    void collectPaymentDetails();
}

package refactoring_guru.strategy.example.strategies;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

/**
 * استراتيجية محددة. تُنفّذ طريقة الدفع عبر PayPal.
 */
public class PayByPayPal implements PayStrategy {
    private static final Map<String, String> DATA_BASE = new HashMap<>();
    private final BufferedReader READER = new BufferedReader(new InputStreamReader(System.in));
    private String email;
    private String password;
    private boolean signedIn;

    static {
        DATA_BASE.put("amanda1985", "amanda@ya.com");
        DATA_BASE.put("qwerty", "john@amazon.eu");
    }

    /**
     * جمع بيانات العميل.
     */
    @Override
    public void collectPaymentDetails() {
        try {
            while (!signedIn) {
                System.out.print("Enter the user's email: ");
                email = READER.readLine();
                System.out.print("Enter the password: ");
                password = READER.readLine();
                if (verify()) {
                    System.out.println("Data verification has been successful.");
                } else {
                    System.out.println("Wrong email or password!");
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    private boolean verify() {
        setSignedIn(email.equals(DATA_BASE.get(password)));
        return signedIn;
    }

    /**
     * حفظ بيانات العميل لمحاولات التسوق المستقبلية.
     */
    @Override
    public boolean pay(int paymentAmount) {
        if (signedIn) {
            System.out.println("Paying " + paymentAmount + " using PayPal.");
            return true;
        } else {
            return false;
        }
    }

    private void setSignedIn(boolean signedIn) {
        this.signedIn = signedIn;
    }
}

package refactoring_guru.strategy.example.strategies;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * استراتيجية محددة. تُنفّذ طريقة الدفع ببطاقة الائتمان.
 */
public class PayByCreditCard implements PayStrategy {
    private final BufferedReader READER = new BufferedReader(new InputStreamReader(System.in));
    private CreditCard card;

    /**
     * جمع بيانات بطاقة الائتمان.
     */
    @Override
    public void collectPaymentDetails() {
        try {
            System.out.print("Enter the card number: ");
            String number = READER.readLine();
            System.out.print("Enter the card expiration date 'mm/yy': ");
            String date = READER.readLine();
            System.out.print("Enter the CVV code: ");
            String cvv = READER.readLine();
            card = new CreditCard(number, date, cvv);

            // التحقق من رقم بطاقة الائتمان...

        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * بعد التحقق من البطاقة يمكننا خصم المبلغ من بطاقة ائتمان العميل.
     */
    @Override
    public boolean pay(int paymentAmount) {
        if (cardIsPresent()) {
            System.out.println("Paying " + paymentAmount + " using Credit Card.");
            card.setAmount(card.getAmount() - paymentAmount);
            return true;
        } else {
            return false;
        }
    }

    private boolean cardIsPresent() {
        return card != null;
    }
}

package refactoring_guru.strategy.example.strategies;

/**
 * فئة بطاقة ائتمان وهمية.
 */
public class CreditCard {
    private int amount;
    private String number;
    private String date;
    private String cvv;

    CreditCard(String number, String date, String cvv) {
        this.amount = 100_000;
        this.number = number;
        this.date = date;
        this.cvv = cvv;
    }

    public void setAmount(int amount) {
        this.amount = amount;
    }

    public int getAmount() {
        return amount;
    }
}

package refactoring_guru.strategy.example.order;

import refactoring_guru.strategy.example.strategies.PayStrategy;

/**
 * فئة الطلب. لا تعرف طريقة الدفع (الاستراتيجية) المحددة التي اختارها المستخدم.
 * تستخدم واجهة الاستراتيجية المشتركة لتفويض جمع بيانات الدفع إلى كائن الاستراتيجية.
 * يمكن استخدامها لحفظ الطلب في قاعدة البيانات.
 */
public class Order {
    private int totalCost = 0;
    private boolean isClosed = false;

    public void processOrder(PayStrategy strategy) {
        strategy.collectPaymentDetails();
        // هنا يمكننا جمع وتخزين بيانات الدفع من الاستراتيجية.
    }

    public void setTotalCost(int cost) {
        this.totalCost += cost;
    }

    public int getTotalCost() {
        return totalCost;
    }

    public boolean isClosed() {
        return isClosed;
    }

    public void setClosed() {
        isClosed = true;
    }
}

package refactoring_guru.strategy.example;

import refactoring_guru.strategy.example.order.Order;
import refactoring_guru.strategy.example.strategies.PayByCreditCard;
import refactoring_guru.strategy.example.strategies.PayByPayPal;
import refactoring_guru.strategy.example.strategies.PayStrategy;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

/**
 * أول تطبيق تجارة إلكترونية عبر سطر الأوامر في العالم.
 */
public class Demo {
    private static Map<Integer, Integer> priceOnProducts = new HashMap<>();
    private static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    private static Order order = new Order();
    private static PayStrategy strategy;

    static {
        priceOnProducts.put(1, 2200);
        priceOnProducts.put(2, 1850);
        priceOnProducts.put(3, 1100);
        priceOnProducts.put(4, 890);
    }

    public static void main(String[] args) throws IOException {
        while (!order.isClosed()) {
            int cost;

            String continueChoice;
            do {
                System.out.print("Please, select a product:" + "\n" +
                        "1 - Mother board" + "\n" +
                        "2 - CPU" + "\n" +
                        "3 - HDD" + "\n" +
                        "4 - Memory" + "\n");
                int choice = Integer.parseInt(reader.readLine());
                cost = priceOnProducts.get(choice);
                System.out.print("Count: ");
                int count = Integer.parseInt(reader.readLine());
                order.setTotalCost(cost * count);
                System.out.print("Do you wish to continue selecting products? Y/N: ");
                continueChoice = reader.readLine();
            } while (continueChoice.equalsIgnoreCase("Y"));

            if (strategy == null) {
                System.out.println("Please, select a payment method:" + "\n" +
                        "1 - PalPay" + "\n" +
                        "2 - Credit Card");
                String paymentMethod = reader.readLine();

                // يُنشئ العميل استراتيجيات مختلفة بناءً على إدخال المستخدم
                // وإعدادات التطبيق وما إلى ذلك.
                if (paymentMethod.equals("1")) {
                    strategy = new PayByPayPal();
                } else {
                    strategy = new PayByCreditCard();
                }
            }

            // يفوّض كائن الطلب جمع بيانات الدفع إلى كائن الاستراتيجية،
            // لأن الاستراتيجيات وحدها تعرف البيانات التي تحتاجها
            // لمعالجة الدفع.
            order.processOrder(strategy);

            System.out.print("Pay " + order.getTotalCost() + " units or Continue shopping? P/C: ");
            String proceed = reader.readLine();
            if (proceed.equalsIgnoreCase("P")) {
                // أخيراً، تتولى الاستراتيجية معالجة الدفع.
                if (strategy.pay(order.getTotalCost())) {
                    System.out.println("Payment has been successful.");
                } else {
                    System.out.println("FAIL! Please, check your data.");
                }
                order.setClosed();
            }
        }
    }
}

Please, select a product:
1 - Mother board
2 - CPU
3 - HDD
4 - Memory
1
Count: 2
Do you wish to continue selecting products? Y/N: y
Please, select a product:
1 - Mother board
2 - CPU
3 - HDD
4 - Memory
2
Count: 1
Do you wish to continue selecting products? Y/N: n
Please, select a payment method:
1 - PalPay
2 - Credit Card
1
Enter the user's email: user@example.com
Enter the password: qwerty
Wrong email or password!
Enter user email: amanda@ya.com
Enter password: amanda1985
Data verification has been successful.
Pay 6250 units or Continue shopping?  P/C: p
Paying 6250 using PayPal.
Payment has been successful.
```

### csharp

```csharp
using System;
using System.Collections.Generic;

namespace RefactoringGuru.DesignPatterns.Strategy.Conceptual
{
    // يُحدد Context الواجهة التي تهم العملاء.
    class Context
    {
        // يحتفظ Context بمرجع إلى أحد كائنات Strategy. لا يعرف
        // Context الفئة المحددة للاستراتيجية. يجب أن يعمل
        // مع جميع الاستراتيجيات عبر واجهة Strategy.
        private IStrategy _strategy;

        public Context()
        { }

        // عادةً، يقبل Context استراتيجية عبر المُنشئ، لكنه
        // يوفر أيضاً دالة ضبط لتغييرها أثناء وقت التشغيل.
        public Context(IStrategy strategy)
        {
            this._strategy = strategy;
        }

        // عادةً، يسمح Context باستبدال كائن Strategy أثناء وقت التشغيل.
        public void SetStrategy(IStrategy strategy)
        {
            this._strategy = strategy;
        }

        // يفوّض Context بعض العمل إلى كائن Strategy بدلاً من
        // تنفيذ نسخ متعددة من الخوارزمية بنفسه.
        public void DoSomeBusinessLogic()
        {
            Console.WriteLine("Context: Sorting data using the strategy (not sure how it'll do it)");
            var result = this._strategy.DoAlgorithm(new List<string> { "a", "b", "c", "d", "e" });

            string resultStr = string.Empty;
            foreach (var element in result as List<string>)
            {
                resultStr += element + ",";
            }

            Console.WriteLine(resultStr);
        }
    }

    // تُعلن واجهة Strategy عن العمليات المشتركة لجميع الإصدارات
    // المدعومة من خوارزمية ما.
    //
    // يستخدم Context هذه الواجهة لاستدعاء الخوارزمية المُعرَّفة
    // بواسطة الاستراتيجيات المحددة.
    public interface IStrategy
    {
        object DoAlgorithm(object data);
    }

    // تُنفّذ الاستراتيجيات المحددة الخوارزمية مع الالتزام بواجهة
    // Strategy الأساسية. تجعلها الواجهة قابلة للتبادل في Context.
    class ConcreteStrategyA : IStrategy
    {
        public object DoAlgorithm(object data)
        {
            var list = data as List<string>;
            list.Sort();

            return list;
        }
    }

    class ConcreteStrategyB : IStrategy
    {
        public object DoAlgorithm(object data)
        {
            var list = data as List<string>;
            list.Sort();
            list.Reverse();

            return list;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // يختار كود العميل استراتيجية محددة ويمررها إلى السياق.
            // يجب أن يكون العميل على دراية بالفروق بين الاستراتيجيات
            // لاتخاذ الاختيار الصحيح.
            var context = new Context();

            Console.WriteLine("Client: Strategy is set to normal sorting.");
            context.SetStrategy(new ConcreteStrategyA());
            context.DoSomeBusinessLogic();
            
            Console.WriteLine();
            
            Console.WriteLine("Client: Strategy is set to reverse sorting.");
            context.SetStrategy(new ConcreteStrategyB());
            context.DoSomeBusinessLogic();
        }
    }
}

Client: Strategy is set to normal sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
a,b,c,d,e

Client: Strategy is set to reverse sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
e,d,c,b,a
```

### cpp

```cpp
/**
 * تُعلن واجهة Strategy عن العمليات المشتركة لجميع الإصدارات المدعومة
 * من خوارزمية ما.
 *
 * يستخدم Context هذه الواجهة لاستدعاء الخوارزمية المُعرَّفة بواسطة
 * الاستراتيجيات المحددة.
 */
class Strategy
{
public:
    virtual ~Strategy() = default;
    virtual std::string doAlgorithm(std::string_view data) const = 0;
};

/**
 * يُحدد Context الواجهة التي تهم العملاء.
 */

class Context
{
    /**
     * @var Strategy يحتفظ Context بمرجع إلى أحد كائنات Strategy.
     * لا يعرف Context الفئة المحددة للاستراتيجية. يجب أن يعمل
     * مع جميع الاستراتيجيات عبر واجهة Strategy.
     */
private:
    std::unique_ptr<Strategy> strategy_;
    /**
     * عادةً، يقبل Context استراتيجية عبر المُنشئ، لكنه يوفر أيضاً
     * دالة ضبط لتغييرها أثناء وقت التشغيل.
     */
public:
    explicit Context(std::unique_ptr<Strategy> &&strategy = {}) : strategy_(std::move(strategy))
    {
    }
    /**
     * عادةً، يسمح Context باستبدال كائن Strategy أثناء وقت التشغيل.
     */
    void set_strategy(std::unique_ptr<Strategy> &&strategy)
    {
        strategy_ = std::move(strategy);
    }
    /**
     * يفوّض Context بعض العمل إلى كائن Strategy بدلاً من
     * تنفيذ نسخ متعددة من الخوارزمية بنفسه.
     */
    void doSomeBusinessLogic() const
    {
        if (strategy_) {
            std::cout << "Context: Sorting data using the strategy (not sure how it'll do it)\n";
            std::string result = strategy_->doAlgorithm("aecbd");
            std::cout << result << "\n";
        } else {
            std::cout << "Context: Strategy isn't set\n";
        }
    }
};

/**
 * تُنفّذ الاستراتيجيات المحددة الخوارزمية مع الالتزام بواجهة Strategy
 * الأساسية. تجعلها الواجهة قابلة للتبادل في Context.
 */
class ConcreteStrategyA : public Strategy
{
public:
    std::string doAlgorithm(std::string_view data) const override
    {
        std::string result(data);
        std::sort(std::begin(result), std::end(result));

        return result;
    }
};
class ConcreteStrategyB : public Strategy
{
    std::string doAlgorithm(std::string_view data) const override
    {
        std::string result(data);
        std::sort(std::begin(result), std::end(result), std::greater<>());

        return result;
    }
};
/**
 * يختار كود العميل استراتيجية محددة ويمررها إلى السياق. يجب أن
 * يكون العميل على دراية بالفروق بين الاستراتيجيات لاتخاذ الاختيار الصحيح.
 */

void clientCode()
{
    Context context(std::make_unique<ConcreteStrategyA>());
    std::cout << "Client: Strategy is set to normal sorting.\n";
    context.doSomeBusinessLogic();
    std::cout << "\n";
    std::cout << "Client: Strategy is set to reverse sorting.\n";
    context.set_strategy(std::make_unique<ConcreteStrategyB>());
    context.doSomeBusinessLogic();
}

int main()
{
    clientCode();
    return 0;
}

Client: Strategy is set to normal sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
abcde

Client: Strategy is set to reverse sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
edcba
```

### go

```go
package main

type EvictionAlgo interface {
	evict(c *Cache)
}

package main

import "fmt"

type Fifo struct {
}

func (l *Fifo) evict(c *Cache) {
	fmt.Println("Evicting by fifo strtegy")
}

package main

import "fmt"

type Lru struct {
}

func (l *Lru) evict(c *Cache) {
	fmt.Println("Evicting by lru strtegy")
}

package main

import "fmt"

type Lfu struct {
}

func (l *Lfu) evict(c *Cache) {
	fmt.Println("Evicting by lfu strtegy")
}

package main

type Cache struct {
	storage      map[string]string
	evictionAlgo EvictionAlgo
	capacity     int
	maxCapacity  int
}

func initCache(e EvictionAlgo) *Cache {
	storage := make(map[string]string)
	return &Cache{
		storage:      storage,
		evictionAlgo: e,
		capacity:     0,
		maxCapacity:  2,
	}
}

func (c *Cache) setEvictionAlgo(e EvictionAlgo) {
	c.evictionAlgo = e
}

func (c *Cache) add(key, value string) {
	if c.capacity == c.maxCapacity {
		c.evict()
	}
	c.capacity++
	c.storage[key] = value
}

func (c *Cache) get(key string) {
	delete(c.storage, key)
}

func (c *Cache) evict() {
	c.evictionAlgo.evict(c)
	c.capacity--
}

package main

func main() {
	lfu := &Lfu{}
	cache := initCache(lfu)

	cache.add("a", "1")
	cache.add("b", "2")

	cache.add("c", "3")

	lru := &Lru{}
	cache.setEvictionAlgo(lru)

	cache.add("d", "4")

	fifo := &Fifo{}
	cache.setEvictionAlgo(fifo)

	cache.add("e", "5")

}

Evicting by lfu strtegy
Evicting by lru strtegy
Evicting by fifo strtegy
```

### php

```php
<?php

namespace RefactoringGuru\Strategy\Conceptual;

/**
 * يُحدد Context الواجهة التي تهم العملاء.
 */
class Context
{
    /**
     * @var Strategy يحتفظ Context بمرجع إلى أحد كائنات Strategy.
     * لا يعرف Context الفئة المحددة للاستراتيجية. يجب أن يعمل
     * مع جميع الاستراتيجيات عبر واجهة Strategy.
     */
    private $strategy;

    /**
     * عادةً، يقبل Context استراتيجية عبر المُنشئ، لكنه يوفر أيضاً
     * دالة ضبط لتغييرها أثناء وقت التشغيل.
     */
    public function __construct(Strategy $strategy)
    {
        $this->strategy = $strategy;
    }

    /**
     * عادةً، يسمح Context باستبدال كائن Strategy أثناء وقت التشغيل.
     */
    public function setStrategy(Strategy $strategy)
    {
        $this->strategy = $strategy;
    }

    /**
     * يفوّض Context بعض العمل إلى كائن Strategy بدلاً من
     * تنفيذ نسخ متعددة من الخوارزمية بنفسه.
     */
    public function doSomeBusinessLogic(): void
    {
        // ...

        echo "Context: Sorting data using the strategy (not sure how it'll do it)\n";
        $result = $this->strategy->doAlgorithm(["a", "b", "c", "d", "e"]);
        echo implode(",", $result) . "\n";

        // ...
    }
}

/**
 * تُعلن واجهة Strategy عن العمليات المشتركة لجميع الإصدارات المدعومة
 * من خوارزمية ما.
 *
 * يستخدم Context هذه الواجهة لاستدعاء الخوارزمية المُعرَّفة بواسطة
 * الاستراتيجيات المحددة.
 */
interface Strategy
{
    public function doAlgorithm(array $data): array;
}

/**
 * تُنفّذ الاستراتيجيات المحددة الخوارزمية مع الالتزام بواجهة Strategy
 * الأساسية. تجعلها الواجهة قابلة للتبادل في Context.
 */
class ConcreteStrategyA implements Strategy
{
    public function doAlgorithm(array $data): array
    {
        sort($data);

        return $data;
    }
}

class ConcreteStrategyB implements Strategy
{
    public function doAlgorithm(array $data): array
    {
        rsort($data);

        return $data;
    }
}

/**
 * يختار كود العميل استراتيجية محددة ويمررها إلى السياق. يجب أن
 * يكون العميل على دراية بالفروق بين الاستراتيجيات لاتخاذ الاختيار الصحيح.
 */
$context = new Context(new ConcreteStrategyA());
echo "Client: Strategy is set to normal sorting.\n";
$context->doSomeBusinessLogic();

echo "\n";

echo "Client: Strategy is set to reverse sorting.\n";
$context->setStrategy(new ConcreteStrategyB());
$context->doSomeBusinessLogic();

Client: Strategy is set to normal sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
a,b,c,d,e

Client: Strategy is set to reverse sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
e,d,c,b,a

<?php

namespace RefactoringGuru\Strategy\RealWorld;

/**
 * هذا هو الموجّه والمتحكم في تطبيقنا. عند استلام طلب، تقرر هذه الفئة
 * أي سلوك يجب تنفيذه. عندما يستلم التطبيق طلب دفع، تقرر فئة OrderController
 * أيضاً طريقة الدفع التي ستستخدمها لمعالجة الطلب. وبذلك تعمل الفئة
 * كسياق وعميل في الوقت ذاته.
 */
class OrderController
{
    /**
     * معالجة طلبات POST.
     *
     * @param $url
     * @param $data
     * @throws \Exception
     */
    public function post(string $url, array $data)
    {
        echo "Controller: POST request to $url with " . json_encode($data) . "\n";

        $path = parse_url($url, PHP_URL_PATH);

        if (preg_match('#^/orders?$#', $path, $matches)) {
            $this->postNewOrder($data);
        } else {
            echo "Controller: 404 page\n";
        }
    }

    /**
     * معالجة طلبات GET.
     *
     * @param $url
     * @throws \Exception
     */
    public function get(string $url): void
    {
        echo "Controller: GET request to $url\n";

        $path = parse_url($url, PHP_URL_PATH);
        $query = parse_url($url, PHP_URL_QUERY);
        parse_str($query, $data);

        if (preg_match('#^/orders?$#', $path, $matches)) {
            $this->getAllOrders();
        } elseif (preg_match('#^/order/([0-9]+?)/payment/([a-z]+?)(/return)?$#', $path, $matches)) {
            $order = Order::get($matches[1]);

            // يُختار أسلوب الدفع (الاستراتيجية) وفقاً للقيمة
            // المُمررة مع الطلب.
            $paymentMethod = PaymentFactory::getPaymentMethod($matches[2]);

            if (!isset($matches[3])) {
                $this->getPayment($paymentMethod, $order, $data);
            } else {
                $this->getPaymentReturn($paymentMethod, $order, $data);
            }
        } else {
            echo "Controller: 404 page\n";
        }
    }

    /**
     * POST /order {data}
     */
    public function postNewOrder(array $data): void
    {
        $order = new Order($data);
        echo "Controller: Created the order #{$order->id}.\n";
    }

    /**
     * GET /orders
     */
    public function getAllOrders(): void
    {
        echo "Controller: Here's all orders:\n";
        foreach (Order::get() as $order) {
            echo json_encode($order, JSON_PRETTY_PRINT) . "\n";
        }
    }

    /**
     * GET /order/123/payment/XX
     */
    public function getPayment(PaymentMethod $method, Order $order, array $data): void
    {
        // يُفوَّض العمل الفعلي إلى كائن أسلوب الدفع.
        $form = $method->getPaymentForm($order);
        echo "Controller: here's the payment form:\n";
        echo $form . "\n";
    }

    /**
     * GET /order/123/payment/XXX/return?key=AJHKSJHJ3423&success=true
     */
    public function getPaymentReturn(PaymentMethod $method, Order $order, array $data): void
    {
        try {
            // نوع آخر من العمل المفوَّض إلى أسلوب الدفع.
            if ($method->validateReturn($order, $data)) {
                echo "Controller: Thanks for your order!\n";
                $order->complete();
            }
        } catch (\Exception $e) {
            echo "Controller: got an exception (" . $e->getMessage() . ")\n";
        }
    }
}

/**
 * تمثيل مبسّط لفئة Order.
 */
class Order
{
    /**
     * للتبسيط، سنخزن جميع الطلبات المنشأة هنا...
     *
     * @var array
     */
    private static $orders = [];

    /**
     * ...وسنصل إليها من هنا.
     *
     * @param int $orderId
     * @return mixed
     */
    public static function get(int $orderId = null)
    {
        if ($orderId === null) {
            return static::$orders;
        } else {
            return static::$orders[$orderId];
        }
    }

    /**
     * يُسند مُنشئ Order قيم حقول الطلب. للتبسيط، لا يوجد أي تحقق.
     *
     * @param array $attributes
     */
    public function __construct(array $attributes)
    {
        $this->id = count(static::$orders);
        $this->status = "new";
        foreach ($attributes as $key => $value) {
            $this->{$key} = $value;
        }
        static::$orders[$this->id] = $this;
    }

    /**
     * الأسلوب الذي يُستدعى عند الدفع مقابل طلب.
     */
    public function complete(): void
    {
        $this->status = "completed";
        echo "Order: #{$this->id} is now {$this->status}.";
    }
}

/**
 * تساعد هذه الفئة في إنتاج كائن استراتيجية مناسب لمعالجة الدفع.
 */
class PaymentFactory
{
    /**
     * الحصول على أسلوب دفع بمعرّفه.
     *
     * @param $id
     * @return PaymentMethod
     * @throws \Exception
     */
    public static function getPaymentMethod(string $id): PaymentMethod
    {
        switch ($id) {
            case "cc":
                return new CreditCardPayment();
            case "paypal":
                return new PayPalPayment();
            default:
                throw new \Exception("Unknown Payment Method");
        }
    }
}

/**
 * تصف واجهة Strategy كيف يمكن للعميل استخدام استراتيجيات محددة متنوعة.
 *
 * لاحظ أنه في معظم الأمثلة الموجودة على الإنترنت، تميل الاستراتيجيات
 * إلى فعل شيء صغير داخل أسلوب واحد. غير أنه في الواقع، يمكن أن تكون
 * استراتيجياتك أكثر قوة بكثير (عن طريق وجود عدة أساليب، على سبيل المثال).
 */
interface PaymentMethod
{
    public function getPaymentForm(Order $order): string;

    public function validateReturn(Order $order, array $data): bool;
}

/**
 * توفر هذه الاستراتيجية المحددة نموذج دفع وتتحقق من المرتجعات
 * لمدفوعات بطاقات الائتمان.
 */
class CreditCardPayment implements PaymentMethod
{
    private static $store_secret_key = "swordfish";

    public function getPaymentForm(Order $order): string
    {
        $returnURL = "https://our-website.com/" .
            "order/{$order->id}/payment/cc/return";

        return <<<FORM
<form action="https://my-credit-card-processor.com/charge" method="POST">
    <input type="hidden" id="email" value="{$order->email}">
    <input type="hidden" id="total" value="{$order->total}">
    <input type="hidden" id="returnURL" value="$returnURL">
    <input type="text" id="cardholder-name">
    <input type="text" id="credit-card">
    <input type="text" id="expiration-date">
    <input type="text" id="ccv-number">
    <input type="submit" value="Pay">
</form>
FORM;
    }

    public function validateReturn(Order $order, array $data): bool
    {
        echo "CreditCardPayment: ...validating... ";

        if ($data['key'] != md5($order->id . static::$store_secret_key)) {
            throw new \Exception("Payment key is wrong.");
        }

        if (!isset($data['success']) || !$data['success'] || $data['success'] == 'false') {
            throw new \Exception("Payment failed.");
        }

        // ...

        if (floatval($data['total']) < $order->total) {
            throw new \Exception("Payment amount is wrong.");
        }

        echo "Done!\n";

        return true;
    }
}

/**
 * توفر هذه الاستراتيجية المحددة نموذج دفع وتتحقق من المرتجعات
 * لمدفوعات PayPal.
 */
class PayPalPayment implements PaymentMethod
{
    public function getPaymentForm(Order $order): string
    {
        $returnURL = "https://our-website.com/" .
            "order/{$order->id}/payment/paypal/return";

        return <<<FORM
<form action="https://paypal.com/payment" method="POST">
    <input type="hidden" id="email" value="{$order->email}">
    <input type="hidden" id="total" value="{$order->total}">
    <input type="hidden" id="returnURL" value="$returnURL">
    <input type="submit" value="Pay on PayPal">
</form>
FORM;
    }

    public function validateReturn(Order $order, array $data): bool
    {
        echo "PayPalPayment: ...validating... ";

        // ...

        echo "Done!\n";

        return true;
    }
}

/**
 * كود العميل.
 */

$controller = new OrderController();

echo "Client: Let's create some orders\n";

$controller->post("/orders", [
    "email" => "me@example.com",
    "product" => "ABC Cat food (XL)",
    "total" => 9.95,
]);

$controller->post("/orders", [
    "email" => "me@example.com",
    "product" => "XYZ Cat litter (XXL)",
    "total" => 19.95,
]);

echo "\nClient: List my orders, please\n";

$controller->get("/orders");

echo "\nClient: I'd like to pay for the second, show me the payment form\n";

$controller->get("/order/1/payment/paypal");

echo "\nClient: ...pushes the Pay button...\n";
echo "\nClient: Oh, I'm redirected to the PayPal.\n";
echo "\nClient: ...pays on the PayPal...\n";
echo "\nClient: Alright, I'm back with you, guys.\n";

$controller->get("/order/1/payment/paypal/return" .
    "?key=c55a3964833a4b0fa4469ea94a057152&success=true&total=19.95");

Client: Let's create some orders
Controller: POST request to /orders with {"email":"me@example.com","product":"ABC Cat food (XL)","total":9.95}
Controller: Created the order #0.
Controller: POST request to /orders with {"email":"me@example.com","product":"XYZ Cat litter (XXL)","total":19.95}
Controller: Created the order #1.

Client: List my orders, please
Controller: GET request to /orders
Controller: Here's all orders:
{
    "id": 0,
    "status": "new",
    "email": "me@example.com",
    "product": "ABC Cat food (XL)",
    "total": 9.95
}
{
    "id": 1,
    "status": "new",
    "email": "me@example.com",
    "product": "XYZ Cat litter (XXL)",
    "total": 19.95
}

Client: I'd like to pay for the second, show me the payment form
Controller: GET request to /order/1/payment/paypal
Controller: here's the payment form:
<form action="https://paypal.com/payment" method="POST">
    <input type="hidden" id="email" value="me@example.com">
    <input type="hidden" id="total" value="19.95">
    <input type="hidden" id="returnURL" value="https://our-website.com/order/1/payment/paypal/return">
    <input type="submit" value="Pay on PayPal">
</form>

Client: ...pushes the Pay button...

Client: Oh, I'm redirected to the PayPal.

Client: ...pays on the PayPal...

Client: Alright, I'm back with you, guys.
Controller: GET request to /order/1/payment/paypal/return?key=c55a3964833a4b0fa4469ea94a057152&success=true&total=19.95
PayPalPayment: ...validating... Done!
Controller: Thanks for your order!
Order: #1 is now completed.
```

### python

```python
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import List


class Context():
    """
    يُحدد Context الواجهة التي تهم العملاء.
    """

    def __init__(self, strategy: Strategy) -> None:
        """
        عادةً، يقبل Context استراتيجية عبر المُنشئ، لكنه يوفر أيضاً
        دالة ضبط لتغييرها أثناء وقت التشغيل.
        """

        self._strategy = strategy

    @property
    def strategy(self) -> Strategy:
        """
        يحتفظ Context بمرجع إلى أحد كائنات Strategy. لا يعرف Context
        الفئة المحددة للاستراتيجية. يجب أن يعمل مع جميع الاستراتيجيات
        عبر واجهة Strategy.
        """

        return self._strategy

    @strategy.setter
    def strategy(self, strategy: Strategy) -> None:
        """
        عادةً، يسمح Context باستبدال كائن Strategy أثناء وقت التشغيل.
        """

        self._strategy = strategy

    def do_some_business_logic(self) -> None:
        """
        يفوّض Context بعض العمل إلى كائن Strategy بدلاً من تنفيذ
        نسخ متعددة من الخوارزمية بنفسه.
        """

        # ...

        print("Context: Sorting data using the strategy (not sure how it'll do it)")
        result = self._strategy.do_algorithm(["a", "b", "c", "d", "e"])
        print(",".join(result))

        # ...


class Strategy(ABC):
    """
    تُعلن واجهة Strategy عن العمليات المشتركة لجميع الإصدارات المدعومة
    من خوارزمية ما.

    يستخدم Context هذه الواجهة لاستدعاء الخوارزمية المُعرَّفة بواسطة
    الاستراتيجيات المحددة.
    """

    @abstractmethod
    def do_algorithm(self, data: List):
        pass


"""
تُنفّذ الاستراتيجيات المحددة الخوارزمية مع الالتزام بواجهة Strategy
الأساسية. تجعلها الواجهة قابلة للتبادل في Context.
"""


class ConcreteStrategyA(Strategy):
    def do_algorithm(self, data: List) -> List:
        return sorted(data)


class ConcreteStrategyB(Strategy):
    def do_algorithm(self, data: List) -> List:
        return reversed(sorted(data))


if __name__ == "__main__":
    # يختار كود العميل استراتيجية محددة ويمررها إلى السياق.
    # يجب أن يكون العميل على دراية بالفروق بين الاستراتيجيات
    # لاتخاذ الاختيار الصحيح.

    context = Context(ConcreteStrategyA())
    print("Client: Strategy is set to normal sorting.")
    context.do_some_business_logic()
    print()

    print("Client: Strategy is set to reverse sorting.")
    context.strategy = ConcreteStrategyB()
    context.do_some_business_logic()

Client: Strategy is set to normal sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
a,b,c,d,e

Client: Strategy is set to reverse sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
e,d,c,b,a
```

### ruby

```ruby
# يُحدد Context الواجهة التي تهم العملاء.
class Context
  # يحتفظ Context بمرجع إلى أحد كائنات Strategy. لا يعرف Context
  # الفئة المحددة للاستراتيجية. يجب أن يعمل مع جميع الاستراتيجيات
  # عبر واجهة Strategy.
  attr_writer :strategy

  # عادةً، يقبل Context استراتيجية عبر المُنشئ، لكنه يوفر أيضاً
  # دالة ضبط لتغييرها أثناء وقت التشغيل.
  def initialize(strategy)
    @strategy = strategy
  end

  # عادةً، يسمح Context باستبدال كائن Strategy أثناء وقت التشغيل.
  def strategy=(strategy)
    @strategy = strategy
  end

  # يفوّض Context بعض العمل إلى كائن Strategy بدلاً من
  # تنفيذ نسخ متعددة من الخوارزمية بنفسه.
  def do_some_business_logic
    # ...

    puts 'Context: Sorting data using the strategy (not sure how it\'ll do it)'
    result = @strategy.do_algorithm(%w[a b c d e])
    print result.join(',')

    # ...
  end
end

# تُعلن واجهة Strategy عن العمليات المشتركة لجميع الإصدارات المدعومة
# من خوارزمية ما.
#
# يستخدم Context هذه الواجهة لاستدعاء الخوارزمية المُعرَّفة بواسطة
# الاستراتيجيات المحددة.
class Strategy
  # @abstract
  #
  # @param [Array] data
  def do_algorithm(_data)
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end

# تُنفّذ الاستراتيجيات المحددة الخوارزمية مع الالتزام بواجهة Strategy
# الأساسية. تجعلها الواجهة قابلة للتبادل في Context.

class ConcreteStrategyA < Strategy
  # @param [Array] data
  #
  # @return [Array]
  def do_algorithm(data)
    data.sort
  end
end

class ConcreteStrategyB < Strategy
  # @param [Array] data
  #
  # @return [Array]
  def do_algorithm(data)
    data.sort.reverse
  end
end

# يختار كود العميل استراتيجية محددة ويمررها إلى السياق. يجب أن
# يكون العميل على دراية بالفروق بين الاستراتيجيات لاتخاذ الاختيار الصحيح.

context = Context.new(ConcreteStrategyA.new)
puts 'Client: Strategy is set to normal sorting.'
context.do_some_business_logic
puts "\n\n"

puts 'Client: Strategy is set to reverse sorting.'
context.strategy = ConcreteStrategyB.new
context.do_some_business_logic

Client: Strategy is set to normal sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
a,b,c,d,e

Client: Strategy is set to reverse sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
e,d,c,b,a
```

### rust

```rust
/// تُعرِّف استراتيجية قابلة للحقن لبناء المسارات.
trait RouteStrategy {
    fn build_route(&self, from: &str, to: &str);
}

struct WalkingStrategy;

impl RouteStrategy for WalkingStrategy {
    fn build_route(&self, from: &str, to: &str) {
        println!("Walking route from {} to {}: 4 km, 30 min", from, to);
    }
}

struct PublicTransportStrategy;

impl RouteStrategy for PublicTransportStrategy {
    fn build_route(&self, from: &str, to: &str) {
        println!(
            "Public transport route from {} to {}: 3 km, 5 min",
            from, to
        );
    }
}

struct Navigator<T: RouteStrategy> {
    route_strategy: T,
}

impl<T: RouteStrategy> Navigator<T> {
    pub fn new(route_strategy: T) -> Self {
        Self { route_strategy }
    }

    pub fn route(&self, from: &str, to: &str) {
        self.route_strategy.build_route(from, to);
    }
}

fn main() {
    let navigator = Navigator::new(WalkingStrategy);
    navigator.route("Home", "Club");
    navigator.route("Club", "Work");

    let navigator = Navigator::new(PublicTransportStrategy);
    navigator.route("Home", "Club");
    navigator.route("Club", "Work");
}

Walking route from Home to Club: 4 km, 30 min
Walking route from Club to Work: 4 km, 30 min
Public transport route from Home to Club: 3 km, 5 min
Public transport route from Club to Work: 3 km, 5 min

let a = [0i32, 1, 2];

let mut iter = a.iter().filter(|x| x.is_positive());

type RouteStrategy = fn(from: &str, to: &str);

fn walking_strategy(from: &str, to: &str) {
    println!("Walking route from {} to {}: 4 km, 30 min", from, to);
}

fn public_transport_strategy(from: &str, to: &str) {
    println!(
        "Public transport route from {} to {}: 3 km, 5 min",
        from, to
    );
}

struct Navigator {
    route_strategy: RouteStrategy,
}

impl Navigator {
    pub fn new(route_strategy: RouteStrategy) -> Self {
        Self { route_strategy }
    }

    pub fn route(&self, from: &str, to: &str) {
        (self.route_strategy)(from, to);
    }
}

fn main() {
    let navigator = Navigator::new(walking_strategy);
    navigator.route("Home", "Club");
    navigator.route("Club", "Work");

    let navigator = Navigator::new(public_transport_strategy);
    navigator.route("Home", "Club");
    navigator.route("Club", "Work");

    let navigator = Navigator::new(|from, to| println!("Specific route from {} to {}", from, to));
    navigator.route("Home", "Club");
    navigator.route("Club", "Work");
}

Walking route from Home to Club: 4 km, 30 min
Walking route from Club to Work: 4 km, 30 min
Public transport route from Home to Club: 3 km, 5 min
Public transport route from Club to Work: 3 km, 5 min
Specific route from Home to Club
Specific route from Club to Work
```

### swift

```swift
import XCTest

/// يُحدد Context الواجهة التي تهم العملاء.
class Context {

    /// يحتفظ Context بمرجع إلى أحد كائنات Strategy. لا يعرف Context
    /// الفئة المحددة للاستراتيجية. يجب أن يعمل مع جميع الاستراتيجيات
    /// عبر واجهة Strategy.
    private var strategy: Strategy

    /// عادةً، يقبل Context استراتيجية عبر المُنشئ، لكنه يوفر أيضاً
    /// دالة ضبط لتغييرها أثناء وقت التشغيل.
    init(strategy: Strategy) {
        self.strategy = strategy
    }

    /// عادةً، يسمح Context باستبدال كائن Strategy أثناء وقت التشغيل.
    func update(strategy: Strategy) {
        self.strategy = strategy
    }

    /// يفوّض Context بعض العمل إلى كائن Strategy بدلاً من
    /// تنفيذ نسخ متعددة من الخوارزمية بنفسه.
    func doSomeBusinessLogic() {
        print("Context: Sorting data using the strategy (not sure how it'll do it)\n")

        let result = strategy.doAlgorithm(["a", "b", "c", "d", "e"])
        print(result.joined(separator: ","))
    }
}

/// تُعلن واجهة Strategy عن العمليات المشتركة لجميع الإصدارات المدعومة
/// من خوارزمية ما.
///
/// يستخدم Context هذه الواجهة لاستدعاء الخوارزمية المُعرَّفة بواسطة
/// الاستراتيجيات المحددة.
protocol Strategy {

    func doAlgorithm<T: Comparable>(_ data: [T]) -> [T]
}

/// تُنفّذ الاستراتيجيات المحددة الخوارزمية مع الالتزام بواجهة Strategy
/// الأساسية. تجعلها الواجهة قابلة للتبادل في Context.
class ConcreteStrategyA: Strategy {

    func doAlgorithm<T: Comparable>(_ data: [T]) -> [T] {
        return data.sorted()
    }
}

class ConcreteStrategyB: Strategy {

    func doAlgorithm<T: Comparable>(_ data: [T]) -> [T] {
        return data.sorted(by: >)
    }
}

/// لنرَ كيف يعمل كل شيء معاً.
class StrategyConceptual: XCTestCase {

    func test() {

        /// يختار كود العميل استراتيجية محددة ويمررها إلى السياق.
        /// يجب أن يكون العميل على دراية بالفروق بين الاستراتيجيات
        /// لاتخاذ الاختيار الصحيح.

        let context = Context(strategy: ConcreteStrategyA())
        print("Client: Strategy is set to normal sorting.\n")
        context.doSomeBusinessLogic()

        print("\nClient: Strategy is set to reverse sorting.\n")
        context.update(strategy: ConcreteStrategyB())
        context.doSomeBusinessLogic()
    }
}

Client: Strategy is set to normal sorting.

Context: Sorting data using the strategy (not sure how it'll do it)

a,b,c,d,e

Client: Strategy is set to reverse sorting.

Context: Sorting data using the strategy (not sure how it'll do it)

e,d,c,b,a

import XCTest

class StrategyRealWorld: XCTestCase {

    /// يُظهر هذا المثال تنفيذاً بسيطاً لمتحكم قائمة قادر على
    /// عرض نماذج من مصادر بيانات مختلفة:
    ///
    /// (MemoryStorage, CoreDataStorage, RealmStorage)

    func test() {

        let controller = ListController()

        let memoryStorage = MemoryStorage<User>()
        memoryStorage.add(usersFromNetwork())

        clientCode(use: controller, with: memoryStorage)

        clientCode(use: controller, with: CoreDataStorage())

        clientCode(use: controller, with: RealmStorage())
    }

    func clientCode(use controller: ListController, with dataSource: DataSource) {

        controller.update(dataSource: dataSource)
        controller.displayModels()
    }

    private func usersFromNetwork() -> [User] {
        let firstUser = User(id: 1, username: "username1")
        let secondUser = User(id: 2, username: "username2")
        return [firstUser, secondUser]
    }
}

class ListController {

    private var dataSource: DataSource?

    func update(dataSource: DataSource) {
        /// ... إعادة تعيين الحالات الحالية ...
        self.dataSource = dataSource
    }

    func displayModels() {

        guard let dataSource = dataSource else { return }
        let models = dataSource.loadModels() as [User]

        /// ربط النماذج بخلايا عرض القائمة...
        print("\nListController: Displaying models...")
        models.forEach({ print($0) })
    }
}

protocol DataSource {

    func loadModels<T: DomainModel>() -> [T]
}

class MemoryStorage<Model>: DataSource {

    private lazy var items = [Model]()

    func add(_ items: [Model]) {
        self.items.append(contentsOf: items)
    }

    func loadModels<T: DomainModel>() -> [T] {
        guard T.self == User.self else { return [] }
        return items as! [T]
    }
}

class CoreDataStorage: DataSource {

    func loadModels<T: DomainModel>() -> [T] {
        guard T.self == User.self else { return [] }

        let firstUser = User(id: 3, username: "username3")
        let secondUser = User(id: 4, username: "username4")

        return [firstUser, secondUser] as! [T]
    }
}

class RealmStorage: DataSource {

    func loadModels<T: DomainModel>() -> [T] {
        guard T.self == User.self else { return [] }

        let firstUser = User(id: 5, username: "username5")
        let secondUser = User(id: 6, username: "username6")

        return [firstUser, secondUser] as! [T]
    }
}

protocol DomainModel {

    var id: Int { get }
}

struct User: DomainModel {

    var id: Int
    var username: String
}

ListController: Displaying models...
User(id: 1, username: "username1")
User(id: 2, username: "username2")

ListController: Displaying models...
User(id: 3, username: "username3")
User(id: 4, username: "username4")

ListController: Displaying models...
User(id: 5, username: "username5")
User(id: 6, username: "username6")
```

### typescript

```typescript
/**
 * يُحدد Context الواجهة التي تهم العملاء.
 */
class Context {
    /**
     * @type {Strategy} يحتفظ Context بمرجع إلى أحد كائنات Strategy.
     * لا يعرف Context الفئة المحددة للاستراتيجية. يجب أن يعمل
     * مع جميع الاستراتيجيات عبر واجهة Strategy.
     */
    private strategy: Strategy;

    /**
     * عادةً، يقبل Context استراتيجية عبر المُنشئ، لكنه يوفر أيضاً
     * دالة ضبط لتغييرها أثناء وقت التشغيل.
     */
    constructor(strategy: Strategy) {
        this.strategy = strategy;
    }

    /**
     * عادةً، يسمح Context باستبدال كائن Strategy أثناء وقت التشغيل.
     */
    public setStrategy(strategy: Strategy) {
        this.strategy = strategy;
    }

    /**
     * يفوّض Context بعض العمل إلى كائن Strategy بدلاً من
     * تنفيذ نسخ متعددة من الخوارزمية بنفسه.
     */
    public doSomeBusinessLogic(): void {
        // ...

        console.log('Context: Sorting data using the strategy (not sure how it\'ll do it)');
        const result = this.strategy.doAlgorithm(['a', 'b', 'c', 'd', 'e']);
        console.log(result.join(','));

        // ...
    }
}

/**
 * تُعلن واجهة Strategy عن العمليات المشتركة لجميع الإصدارات المدعومة
 * من خوارزمية ما.
 *
 * يستخدم Context هذه الواجهة لاستدعاء الخوارزمية المُعرَّفة بواسطة
 * الاستراتيجيات المحددة.
 */
interface Strategy {
    doAlgorithm(data: string[]): string[];
}

/**
 * تُنفّذ الاستراتيجيات المحددة الخوارزمية مع الالتزام بواجهة Strategy
 * الأساسية. تجعلها الواجهة قابلة للتبادل في Context.
 */
class ConcreteStrategyA implements Strategy {
    public doAlgorithm(data: string[]): string[] {
        return data.sort();
    }
}

class ConcreteStrategyB implements Strategy {
    public doAlgorithm(data: string[]): string[] {
        return data.reverse();
    }
}

/**
 * يختار كود العميل استراتيجية محددة ويمررها إلى السياق. يجب أن
 * يكون العميل على دراية بالفروق بين الاستراتيجيات لاتخاذ الاختيار الصحيح.
 */
const context = new Context(new ConcreteStrategyA());
console.log('Client: Strategy is set to normal sorting.');
context.doSomeBusinessLogic();

console.log('');

console.log('Client: Strategy is set to reverse sorting.');
context.setStrategy(new ConcreteStrategyB());
context.doSomeBusinessLogic();

Client: Strategy is set to normal sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
a,b,c,d,e

Client: Strategy is set to reverse sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
e,d,c,b,a
```

