الوحدة 7: جمل التحكم الشرطية (If-Else, Switch)

كيف تجعل برامجك تتخذ القرارات وتستجيب بذكاء

المدة: أسبوعان 6 ساعات نظرية 6 ساعات عملية

المقدمة والأهداف التعليمية

بعد أن تعلمنا في الوحدات السابقة كيفية التعامل مع البيانات والمتغيرات، وكيفية استخدام العوامل لإجراء العمليات الحسابية والمنطقية، حان الوقت لننتقل إلى مستوى متقدم يجعل برامجنا أكثر تفاعلاً وذكاءً. في هذه الوحدة، سنكتشف **جمل التحكم الشرطية (Conditional Control Statements)**، وهي اللبنة الأساسية لجعل برامجك تتخذ القرارات وتغير سلوكها بناءً على شروط معينة.

سنركز على فهم عميق لجملتي `if-else` بجميع أشكالها، وكيفية استخدام جملة `switch` كبديل أنيق في بعض السيناريوهات. هذه الأدوات ضرورية لبناء أي منطق معقد في تطبيقاتك.

الأهداف:

بنهاية هذه الوحدة، ستكون قادرًا على:

  • فهم الغرض من استخدام الشروط في البرمجة.
  • استخدام جملة if الأساسية لتنفيذ كود مشروط.
  • تطبيق جملة if-else لتوفير مسار بديل عند عدم تحقق الشرط.
  • استخدام جملة else-if للتعامل مع شروط متعددة ومتتالية.
  • بناء شروط مركبة باستخدام العوامل المنطقية (&&، ||، !).
  • فهم وتطبيق الشروط المتداخلة (Nested if).
  • استخدام جملة switch كبديل فعال لـ if-else if المتعددة في حالات معينة.
  • فهم أهمية ودور كلمة break في جملة switch.
  • معرفة متى تستخدم if-else ومتى تستخدم switch.
---

أولاً: مقدمة عن الشروط في جافا

تُعد **الشروط (Conditions)** هي الأساس الذي يجعل البرامج "ذكية" وقادرة على اتخاذ القرارات. بدون الشروط، سيكون البرنامج مجرد سلسلة أوامر ثابتة تُنفذ دون أي اعتبار لمدخلات المستخدم أو التغيرات التي تحدث أثناء تشغيل البرنامج. الشروط تسمح للبرنامج بأن يسلك مسارًا مختلفًا أو ينفذ مجموعة معينة من التعليمات بناءً على صحة تعبير منطقي.

على سبيل المثال: تخيل أنك تصمم برنامجًا يحدد ما إذا كان الطالب ناجحًا أو راسبًا. هنا تحتاج إلى شرط:

  • **إذا** كانت الدرجة أكبر من أو تساوي 50 ← **ناجح**.
  • **وإلا** (إذا كانت أقل من 50) ← **راسب**.

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

---

ثانيًا: جمل `if-else` الأساسية والمتقدمة

جمل `if` و `else` هي الأكثر استخدامًا للتحكم في تدفق البرنامج بناءً على الشروط.

1. الجملة `if` الأساسية

الجملة `if` هي أبسط أشكال الجمل الشرطية. تنفذ مجموعة من التعليمات إذا كان الشرط صحيحًا (`true`).

الصيغة العامة:

if (condition) {
    // الكود الذي سينفذ إذا كان الشرط صحيحًا (true)
}
مثال: التحقق من العمر
public class SimpleIfExample {
    public static void main(String[] args) {
        int age = 20;
        if (age >= 18) {
            System.out.println("يمكنك الدخول إلى الموقع.");
        }
        System.out.println("انتهى البرنامج.");
    }
}

الشرح: هنا، إذا كانت قيمة `age` أكبر من أو تساوي 18، سيتم طباعة رسالة "يمكنك الدخول إلى الموقع.". إذا كانت `age` أقل من 18 (مثلاً 16)، فلن يتم طباعة هذه الرسالة وسينتقل البرنامج مباشرة إلى "انتهى البرنامج.".

2. الجملة `if-else`

تُستخدم جملة `if-else` عندما نريد توفير مسار بديل لتنفيذ الكود في حالة عدم تحقق الشرط الأساسي. فإذا كان الشرط في `if` صحيحًا، يُنفذ الكود داخل `if`، وإلا (أي إذا كان الشرط خاطئًا)، يُنفذ الكود داخل `else`.

الصيغة العامة:

if (condition) {
    // الكود الذي سينفذ إذا كان الشرط صحيحًا (true)
} else {
    // الكود الذي سينفذ إذا كان الشرط خاطئًا (false)
}
مثال: تحديد حالة النجاح/الرسوب
public class IfElseExample {
    public static void main(String[] args) {
        int grade = 45;
        if (grade >= 50) {
            System.out.println("ناجح");
        } else {
            System.out.println("راسب");
        }
    }
}

الشرح: في هذا المثال، بما أن `grade` (45) ليست أكبر من أو تساوي 50، فالشرط خاطئ، وبالتالي سيتم تنفيذ الكود داخل كتلة `else` وستظهر رسالة "راسب".

3. الجملة `else-if`

عندما نحتاج إلى التحقق من أكثر من شرط واحد بطريقة متسلسلة، يمكننا استخدام جمل `else-if`. يتم فحص الشروط بالترتيب، وبمجرد أن يصبح أحد الشروط صحيحًا، يتم تنفيذ كوده وتتجاهل بقية الشروط الأخرى.

الصيغة العامة:

if (condition1) {
    // الكود إذا كان الشرط الأول صحيحًا
} else if (condition2) {
    // الكود إذا كان الشرط الثاني صحيحًا (فقط إذا كان الأول خاطئًا)
} else if (condition3) {
    // الكود إذا كان الشرط الثالث صحيحًا (فقط إذا كان الأول والثاني خاطئين)
} else {
    // الكود إذا لم يتحقق أي من الشروط السابقة
}
مثال: تحديد تقدير الطالب
public class ElseIfExample {
    public static void main(String[] args) {
        int score = 85;
        if (score >= 90) {
            System.out.println("ممتاز");
        } else if (score >= 75) {
            System.out.println("جيد جدًا");
        } else if (score >= 50) {
            System.out.println("ناجح");
        } else {
            System.out.println("راسب");
        }
    }
}

الشرح: في هذا المثال، `score` هي 85. الشرط الأول (`score >= 90`) خاطئ. الشرط الثاني (`score >= 75`) صحيح، لذا سيتم طباعة "جيد جدًا" وسيتوقف فحص باقي الشروط.

4. الشروط المركبة باستخدام العوامل المنطقية

يمكننا دمج عدة شروط بسيطة لإنشاء شروط أكثر تعقيدًا باستخدام **العوامل المنطقية (Logical Operators)** التي تعرفنا عليها في الوحدة السادسة.

  • && (AND): يُرجع true فقط إذا كان **جميع** الشروط المدمجة صحيحة.
  • || (OR): يُرجع true إذا كان **أي واحد** من الشروط المدمجة صحيحًا (أو كلاهما).
  • ! (NOT): يعكس القيمة المنطقية للشرط (إذا كان true يصبح false والعكس).
مثال: التحقق من الأهلية للدخول
public class CompoundConditionExample {
    public static void main(String[] args) {
        int age = 22;
        boolean hasID = true;
        boolean hasTicket = true;

        // استخدام && : يجب أن يكون العمر >= 18 ولديه بطاقة هوية
        if (age >= 18 && hasID) {
            System.out.println("يمكنك الدخول إلى الحدث.");
        } else {
            System.out.println("لا يمكنك الدخول إلى الحدث.");
        }

        System.out.println("---");

        // استخدام || : يجب أن يكون لديه بطاقة هوية أو تذكرة
        if (hasID || hasTicket) {
            System.out.println("يمكنك الدخول (لديك إما هوية أو تذكرة).");
        } else {
            System.out.println("لا يمكنك الدخول (لا هوية ولا تذكرة).");
        }
        
        System.out.println("---");

        // استخدام ! : عكس الشرط
        boolean isLoggedIn = false;
        if (!isLoggedIn) {
            System.out.println("أنت غير مسجل الدخول، يرجى تسجيل الدخول أولاً.");
        } else {
            System.out.println("أنت مسجل الدخول.");
        }
    }
}

5. الشروط المتداخلة (Nested `if`)

الشروط المتداخلة تعني وضع جملة `if` (أو `if-else`) داخل كتلة `if` أو `else` أخرى. تُستخدم هذه الطريقة عندما تحتاج إلى فحص شروط فرعية بعد تحقق شرط رئيسي.

مثال: خصم للطلاب البالغين
public class NestedIfExample {
    public static void main(String[] args) {
        int age = 20;
        boolean isStudent = true;
        boolean hasDiscountCoupon = false;

        if (age >= 18) { // الشرط الرئيسي: هل هو بالغ؟
            System.out.println("الدخول مسموح.");
            if (isStudent) { // الشرط الفرعي 1: هل هو طالب؟
                System.out.println("خصم للطلاب متاح.");
                if (hasDiscountCoupon) { // الشرط الفرعي 2: هل لديه قسيمة خصم إضافية؟
                    System.out.println("مبروك! لديك خصم إضافي.");
                }
            } else {
                System.out.println("الدخول مسموح بدون خصم.");
            }
        } else {
            System.out.println("لا يمكنك الدخول (أقل من 18 عامًا).");
        }
    }
}

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

---

ثالثًا: جملة `switch`

1. مفهوم `switch`

تُستخدم جملة switch عندما نريد اختبار قيمة متغير معين مع عدة احتمالات محددة وثابتة. بدلاً من كتابة سلسلة طويلة من جمل `if-else if-else` عندما يكون لدينا العديد من الشروط المتساوية، يمكننا استخدام `switch` لجعل الكود أكثر وضوحًا وقابلية للقراءة.

تعمل `switch` عن طريق مقارنة قيمة تعبير (عادةً متغير) مع قائمة من قيم case (حالات). عندما يتم العثور على تطابق، يتم تنفيذ التعليمات البرمجية المرتبطة بهذه الـ `case`.

الصيغة العامة:

switch (expression) {
    case value1:
        // الكود الذي سينفذ إذا كانت قيمة التعبير = value1
        break; // مهم للخروج من switch
    case value2:
        // الكود الذي سينفذ إذا كانت قيمة التعبير = value2
        break;
    // ... المزيد من الحالات (cases)
    default:
        // الكود الذي سينفذ إذا لم تتطابق قيمة التعبير مع أي من الحالات السابقة
        break; // اختياري لـ default إذا كان آخر شيء في switch
}
  • **`expression` (التعبير):** هو المتغير أو التعبير الذي سيتم اختباره. في جافا، يمكن أن يكون من أنواع byte، short، char، int، Enum (أنواع التعداد)، String (من Java 7 فما فوق)، أو فئات التغليف (Wrapper Classes) المقابلة (مثل Integer، Byte، إلخ).
  • **`case value:` (الحالة قيمة):** تحدد قيمة معينة ليتم مقارنتها بالتعبير. يجب أن تكون هذه القيم ثابتة (literal) أو ثوابت (final variables).
  • **`break;`:** جملة break مهمة جدًا! تنهي تنفيذ جملة `switch` فورًا وتنتقل بالتحكم إلى الكود الذي يلي `switch`. إذا لم تكتب `break`، سيستمر التنفيذ إلى الحالات التالية (Fall-through)، وهذا قد يؤدي إلى نتائج غير متوقعة.
  • **`default:`:** هذه الكتلة اختيارية. يتم تنفيذ الكود بداخلها إذا لم تتطابق قيمة التعبير مع أي من قيم `case` المحددة. غالبًا ما توضع في النهاية.

2. مثال على `switch` (أيام الأسبوع)

مثال: طباعة اسم اليوم بناءً على رقم
public class SwitchExample {
    public static void main(String[] args) {
        int day = 3; // الرقم 3 يمثل يوم الاثنين

        switch (day) {
            case 1:
                System.out.println("السبت");
                break;
            case 2:
                System.out.println("الأحد");
                break;
            case 3:
                System.out.println("الاثنين");
                break;
            case 4:
                System.out.println("الثلاثاء");
                break;
            case 5:
                System.out.println("الأربعاء");
                break;
            case 6:
                System.out.println("الخميس");
                break;
            case 7:
                System.out.println("الجمعة");
                break;
            default: // في حال لم تتطابق القيمة مع أي من الحالات
                System.out.println("الرقم المدخل لا يمثل يوماً معروفاً.");
                break;
        }
    }
}

الشرح: بما أن `day` تساوي 3، سيتم العثور على تطابق مع `case 3`، وسيتم طباعة "الاثنين". بعد ذلك، جملة `break` ستوقف تنفيذ `switch` وتنتقل إلى نهاية الجملة.

3. ملاحظات مهمة حول `switch`

تذكر النقاط التالية عند استخدام `switch`:

  • **أنواع البيانات المدعومة:** كما ذكرنا، `switch` لا تدعم جميع أنواع البيانات. الأنواع الأولية التي تدعمها هي byte، short، char، و int. كما تدعم Enum و String (من Java 7). لا يمكنك استخدام `long`، `float`، `double`، أو `boolean` مباشرة في تعبير `switch`.
  • **تأثير عدم وجود `break` (Fall-through):** هذه نقطة حرجة. في حالة عدم كتابة `break` بعد كتلة case، سيستمر التنفيذ إلى كتل case التالية حتى يجد جملة break أو يصل إلى نهاية جملة `switch`. هذا السلوك يُعرف بـ "Fall-through".
مثال: `switch` بدون `break` (تأثير Fall-through)
public class SwitchNoBreakExample {
    public static void main(String[] args) {
        int number = 2;

        switch (number) {
            case 1:
                System.out.println("واحد");
            case 2: // هذا الـ case سيتطابق، وسيبدأ التنفيذ من هنا
                System.out.println("اثنان");
            case 3: // لا يوجد break بعد case 2، لذا سيستمر التنفيذ إلى هنا
                System.out.println("ثلاثة");
            default: // ولا هنا، لذا سيستمر التنفيذ إلى default
                System.out.println("أرقام أخرى");
        }
    }
}

الناتج سيكون:

اثنان
ثلاثة
أرقام أخرى

الشرح: بما أن `number` هي 2، سيتم تنفيذ الكود داخل `case 2`. ولأنه لا توجد جملة `break` بعد `case 2`، سيستمر التنفيذ تلقائيًا إلى `case 3` ثم إلى `default`، مما يؤدي إلى طباعة جميع الرسائل اللاحقة.

---

رابعًا: مقارنة بين `if-else` و `switch`

كل من جملتي `if-else` و `switch` تُستخدمان للتحكم في تدفق البرنامج بناءً على الشروط، ولكن لكل منهما استخدامات مفضلة.

  • **متى تستخدم `if-else`؟**
    • عندما تريد التحقق من **شروط معقدة** تتضمن عوامل مقارنة (مثل >، <، >=، <=).
    • عندما تحتاج إلى التحقق من **مدى من القيم** (مثل `if (age >= 18 && age <= 65)`).
    • عندما تتضمن الشروط **تعبيرات منطقية مركبة** باستخدام && و ||.
    • عندما يكون لديك عدد قليل من الشروط.
    • عندما تكون الشروط لا تعتمد على قيمة واحدة ثابتة، بل على تقييم تعبيرات مختلفة.
  • **متى تستخدم `switch`؟**
    • عندما تريد **مطابقة قيمة واحدة** (متغير أو تعبير) مع **عدة احتمالات محددة وثابتة**.
    • عندما تكون هذه الاحتمالات عبارة عن قيم منفصلة (مثل أيام الأسبوع، خيارات قائمة).
    • لجعل الكود **أكثر وضوحًا وتنظيمًا** عندما يكون لديك العديد من `else if` المتتالية التي تختبر نفس المتغير لقيم مختلفة.
    • عند التعامل مع أنواع البيانات التي تدعمها `switch` بشكل مباشر (كما ذكرنا سابقًا: `byte`, `short`, `char`, `int`, `Enum`, `String`).
نصيحة:

تخيل `if-else` كشجرة قرارات يمكنك فيها فحص أي سؤال بأي طريقة. بينما `switch` تشبه مفتاح الاختيار حيث تختار مسارًا واحدًا بناءً على قيمة محددة.

---

خامسًا: أمثلة تطبيقية

المثال الأول: تحديد نوع الحساب (باستخدام `switch` مع `String`)

برنامج يحدد مزايا المستخدم بناءً على نوع حسابه، والذي يتم إدخاله كنص.

public class AccountTypeChecker {
    public static void main(String[] args) {
        String accountType = "VIP"; // يمكن تغيير هذه القيمة للاختبار

        switch (accountType) {
            case "Basic":
                System.out.println("مزايا محدودة: وصول إلى المحتوى الأساسي.");
                break;
            case "Premium":
                System.out.println("مزايا إضافية: وصول إلى المحتوى المميز، دعم فني أسرع.");
                break;
            case "VIP":
                System.out.println("جميع المزايا متاحة: وصول كامل، دعم شخصي 24/7، دعوات حصرية.");
                break;
            default:
                System.out.println("نوع الحساب غير معروف. يرجى التحقق من المدخلات.");
                break;
        }
    }
}

المثال الثاني: التحقق من النجاح وتقدير الطالب (باستخدام `if-else if`)

برنامج يأخذ درجة الطالب ويحدد ما إذا كان ناجحًا أو راسبًا، ويحدد تقديره.

public class StudentGradesChecker {
    public static void main(String[] args) {
        int grade = 72; // يمكن تغيير هذه القيمة للاختبار

        if (grade >= 90) {
            System.out.println("التقدير: امتياز");
        } else if (grade >= 75) {
            System.out.println("التقدير: جيد جدًا");
        } else if (grade >= 50) {
            System.out.println("التقدير: ناجح");
        } else {
            System.out.println("التقدير: راسب");
        }
    }
}

المثال الثالث: تحديد الفئة العمرية (باستخدام `if-else if` و عوامل منطقية)

برنامج يطلب عمر المستخدم ويحدد الفئة العمرية التي ينتمي إليها.

import java.util.Scanner;
import java.util.InputMismatchException;

public class AgeCategoryDeterminer {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("أدخل عمرك: ");

        try {
            int age = scanner.nextInt();

            if (age < 0) {
                System.out.println("عمر غير صالح. العمر لا يمكن أن يكون سالباً.");
            } else if (age <= 12) {
                System.out.println("أنت طفل.");
            } else if (age >= 13 && age <= 19) {
                System.out.println("أنت مراهق.");
            } else if (age >= 20 && age <= 60) {
                System.out.println("أنت بالغ.");
            } else { // age > 60
                System.out.println("أنت كبير في السن.");
            }
        } catch (InputMismatchException e) {
            System.out.println("مدخل غير صالح. يرجى إدخال رقم صحيح للعمر.");
        } finally {
            scanner.close();
        }
    }
}
---

سادسًا: تمارين وتحديات

اختبر فهمك لهذه الوحدة من خلال حل التمارين التالية:

1. تمرين: آلة حاسبة بسيطة (باستخدام `switch` مع `char`)

اكتب برنامج جافا يطلب من المستخدم إدخال رقمين، ثم يطلب منه اختيار عملية حسابية (+، -، *، /) كحرف. استخدم جملة `switch` لتنفيذ العملية المطلوبة وطباعة النتيجة على الكونسول. تأكد من معالجة القسمة على صفر وأنواع المدخلات الخاطئة.

import java.util.Scanner;
import java.util.InputMismatchException;

public class SimpleCalculator {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // اكتب الكود هنا لطلب الرقمين والعملية الحسابية وتنفيذها باستخدام switch
        scanner.close();
    }
}

2. تمرين: تحديد موسم السنة (باستخدام `if-else if`)

اكتب برنامج يطلب من المستخدم إدخال رقم الشهر (من 1 إلى 12). بناءً على رقم الشهر، اطبع اسم الموسم الذي يقع فيه هذا الشهر (مثلاً: الشتاء، الربيع، الصيف، الخريف).
تلميح:

  • الشتاء: 12، 1، 2
  • الربيع: 3، 4، 5
  • الصيف: 6، 7، 8
  • الخريف: 9، 10، 11
تعامل مع المدخلات غير الصالحة.

import java.util.Scanner;
import java.util.InputMismatchException;

public class SeasonDeterminer {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // اكتب الكود هنا لطلب رقم الشهر وتحديد الموسم باستخدام if-else if
        scanner.close();
    }
}

3. تمرين تحدي: التحقق من صلاحية كلمة المرور (باستخدام شروط مركبة)

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

  1. يجب أن تكون كلمة المرور 8 أحرف على الأقل.
  2. يجب أن تحتوي على حرف كبير واحد على الأقل.
  3. يجب أن تحتوي على رقم واحد على الأقل.
اطبع رسالة "كلمة المرور قوية" أو "كلمة المرور ضعيفة" مع تحديد الشروط غير المستوفاة.
تلميح: استخدم دوال مثل `password.length()`، `Character.isUpperCase(char)`، `Character.isDigit(char)`، و عوامل منطقية.

import java.util.Scanner;

public class PasswordStrengthChecker {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("أدخل كلمة المرور: ");
        String password = scanner.nextLine();
        
        boolean isStrong = true;
        String feedback = "";

        // اكتب الكود هنا للتحقق من الشروط وتحديث isStrong و feedback
        // مثال:
        // if (password.length() < 8) {
        //    isStrong = false;
        //    feedback += "يجب أن تكون كلمة المرور 8 أحرف على الأقل.\n";
        // }

        if (isStrong) {
            System.out.println("كلمة المرور قوية!");
        } else {
            System.out.println("كلمة المرور ضعيفة:\n" + feedback);
        }
        scanner.close();
    }
}
---

المراجع

  • موقع Oracle Java Documentation.
  • "Java: A Beginner's Guide" – Herbert Schildt.
  • "Head First Java" – Kathy Sierra, Bert Bates.
---

ملخص الوحدة

في هذه الوحدة الحاسمة، قمتَ ببناء فهم قوي حول **جمل التحكم الشرطية (Conditional Control Statements)** في Java. لقد تعلمتَ كيف تجعل برامجك تتخذ قرارات وتنفذ مسارات مختلفة بناءً على تقييم الشروط.

  • استكشفنا جمل if، if-else، و else-if للتعامل مع الشروط الفردية والمتسلسلة والمعقدة.
  • فهمتَ كيفية دمج الشروط باستخدام العوامل المنطقية (&&، ||، !) لبناء منطق أكثر تعقيدًا.
  • تعرفتَ على جملة switch كبديل أنيق ومباشر لـ if-else if المتعددة عند مقارنة قيمة واحدة باحتمالات ثابتة.
  • أدركتَ الأهمية القصوى لجملة break داخل `switch` لتجنب سلوك "Fall-through" غير المرغوب فيه.
  • تعلمتَ متى تفضل استخدام `if-else` ومتى تكون `switch` هي الخيار الأفضل.

إن إتقان هذه المفاهيم سيمنحك القدرة على كتابة برامج قادرة على التفكير والاستجابة للمدخلات والسيناريوهات المتغيرة، وهي مهارة لا غنى عنها في أي تطبيق برمجي. في الوحدة القادمة، سنتعمق في **حلقات التكرار (Loops)** بأنواعها المختلفة وكيفية استخدامها لتنفيذ التعليمات البرمجية بشكل متكرر وفعال.