تصحيح الأخطاء والوقاية منها
نتتبّع البرنامج خطوة بخطوة لنجد مصدر المشكلة، ونصحّحها، ونتعلّم ممارسات تقلّل الأخطاء من البداية.
- تتبع منهجية من خمس خطوات لتصحيح الأخطاء.
- تتتبّع تنفيذ البرنامج خطوة بخطوة بجدول التتبّع لتحديد مصدر المشكلة.
- تعدّل البرنامج لتصحيح الخطأ ثم تعيد اختباره بجدول حالات.
- تطبّق ممارسات برمجة جيدة تقلل حدوث الأخطاء.
١منهجية التصحيح: خمس خطوات
التصحيح العشوائي، أي تغيير الكود على أمل أن يعمل، يضيّع الوقت وقد يضيف أخطاء جديدة. المبرمج المنظّم يتبع خطوات ثابتة:
| الخطوة | ماذا تفعل؟ |
|---|---|
| ١. أعِد إنتاج الخطأ | حدّد المدخلات التي تُظهر الخطأ بثبات. خطأ لا تستطيع تكراره لا تستطيع إصلاحه. |
| ٢. حدّد المكان | اقرأ رسالة الخطأ، أو استخدم الطباعة ونقاط التوقف لتضييق المكان إلى سطر أو بضعة أسطر. |
| ٣. افهم السبب | لماذا حدث؟ لا تكتفِ بإخفاء العَرَض. مثلًا: لماذا أصبح المقسوم عليه صفرًا؟ |
| ٤. أصلح | عدّل أقل قدر ممكن من الكود، وتغييرًا واحدًا في كل مرة. |
| ٥. أعِد الاختبار | جرّب المدخلات التي أظهرت الخطأ، ثم بقية الحالات للتأكد أنك لم تُفسد شيئًا آخر. |
٢تتبّع التنفيذ خطوة بخطوة
البرنامج التالي يجب أن يعدّ الأعداد الزوجية من 1 إلى 6، ويطبع 3، وهو فعلًا يطبع 3! هل هو صحيح؟ تتبّعه خطوة بخطوة:
int count = 0;for (int i = 1; i <= 6; i++) { if (i % 2 == 1) count++;}System.out.println("عدد الزوجيات = " + count);
الدرس المهم: نتيجة صحيحة مع مدخلات واحدة لا تعني أن البرنامج صحيح. جرّب الحدّ 7 بدل 6: الصحيح 3 زوجيات، والبرنامج يطبع 4.
جدول التتبّع اليدوي Dry Run
بدون حاسب نستطيع تتبّع البرنامج بجدول: عمود لكل متغير وعمود للشرط وعمود للناتج. هذه أقوى أداة تصحيح لأنها تجبرك على التفكير مثل الحاسب.
| i | i % 2 | الشرط i % 2 == 1 | count |
|---|---|---|---|
| 1 | 1 | true | 1 |
| 2 | 0 | false | 1 |
| 3 | 1 | true | 2 |
| 4 | 0 | false | 2 |
| 5 | 1 | true | 3 |
| 6 | 0 | false | 3 |
الجدول يكشف فورًا أن count يزيد مع 1 و 3 و 5، أي الفرديات. الإصلاح: i % 2 == 0.
٣مثال كامل: تصحيح برنامج الفاتورة
المطلوب: اقرأ السعر والكمية، فإذا كان الإجمالي 1000 فأكثر يحصل العميل على خصم 10%. والسعر والكمية يجب أن يكونا أكبر من صفر.
١. نختبر بجدول حالات
| المدخلات | المتوقع | الناتج الفعلي | الحكم |
|---|---|---|---|
| 100 و 5 | الإجمالي: 500.0 | الإجمالي: 500.0 | ✓ |
| 250 و 4 | الإجمالي: 900.0 | الإجمالي: 1000.0 | ✗ |
| 300 و 4 | الإجمالي: 1080.0 | الإجمالي: 1080.0 | ✓ |
| -50 و 2 | قيمة غير صحيحة | الإجمالي: -100.0 | ✗ |
import java.util.Scanner;public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); double price = in.nextDouble(); int qty = in.nextInt(); double total = price * qty; double discount = 0; if (total > 1000) discount = total * 0.1; total = total - discount; System.out.println("الإجمالي: " + total); }}
٢. نحدد المكان ونفهم السبب
- الحالة 250 × 4 = 1000 بالضبط: لم يُطبَّق الخصم. السبب الشرط
total > 1000يستبعد القيمة 1000 نفسها. - الحالة −50: البرنامج قبل سعرًا سالبًا. السبب لا يوجد تحقق من المدخلات.
٣. نصلح ثم نعيد الاختبار
import java.util.Scanner;public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); double price = in.nextDouble(); int qty = in.nextInt(); if (price <= 0 || qty <= 0) { System.out.println("قيمة غير صحيحة"); return; } double total = price * qty; double discount = 0; if (total >= 1000) discount = total * 0.1; total = total - discount; System.out.println("الإجمالي: " + total); }}
import java.util.Scanner;public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); double price = in.nextDouble(); int qty = in.nextInt(); if (price <= 0 || qty <= 0) { System.out.println("قيمة غير صحيحة"); return; } double total = price * qty; double discount = 0; if (total >= 1000) discount = total * 0.1; total = total - discount; System.out.println("الإجمالي: " + total); }}
أعدنا تشغيل كل حالات الجدول، فأصبحت كلها ✓. الكلمة return داخل main تنهي البرنامج مبكرًا.
٤الوقاية: ممارسات البرمجة الجيدة
أفضل خطأ هو الذي لا يحدث. هذه الممارسات تقلل الأخطاء وتجعل اكتشافها أسهل:
| الممارسة | بدلًا من | الأفضل |
|---|---|---|
| أسماء واضحة | double x = a * b; | double total = price * qty; |
| تنسيق ومسافات بادئة | كود متلاصق غير مرتب | Alt + Shift + F في NetBeans |
| ثوابت بدل الأرقام السحرية | total * 0.15 | final double VAT = 0.15; |
| دوال صغيرة | main طويلة من 100 سطر | دالة لكل مهمة، كما في التفكيك |
| التحقق من المدخلات | القسمة مباشرة | if (count != 0) قبل القسمة |
| البناء التدريجي | كتابة البرنامج كاملًا ثم تشغيله | اكتب جزءًا صغيرًا وشغّله، ثم أضف |
| تعليقات مفيدة | i++; // زيادة i | تعليق يشرح السبب لا الواضح |
التحقق من المدخلات قبل القراءة
الدالة hasNextInt() تخبرنا هل ما كتبه المستخدم رقم صحيح قبل قراءته، فنتجنب InputMismatchException:
Scanner in = new Scanner(System.in);System.out.println("ادخل عمرك:");while (!in.hasNextInt()) { System.out.println("أدخل رقمًا صحيحًا من فضلك:"); in.next(); // تجاهل الإدخال الخاطئ}int age = in.nextInt();System.out.println("عمرك " + age);
٥قائمة فحص قبل تسليم أي برنامج
- يُترجَم دون أخطاء، وراجعت التحذيرات الصفراء.
- اختبرته بقيم عادية، وبالقيم الحدّية تمامًا، وبقيم غير صحيحة.
- المخرجات تطابق المطلوب في نص المسألة حرفيًّا.
- حذفت عبارات الطباعة التشخيصية DEBUG.
- الأسماء واضحة والكود منسّق Alt + Shift + F.
- كل جزء من التفكيك له مكان واضح في الكود أو دالة خاصة.
توقّع وتحقّق
ما أول خطوة في منهجية التصحيح؟
برنامج أعطى نتيجة صحيحة مع مدخل واحد. هذا يعني:
ما فائدة hasNextInt()؟
أي اسم متغير أفضل لمجموع الفاتورة؟
الخلاصة
- أعِد إنتاج الخطأ، ثم حدد مكانه، وافهم سببه، وأصلحه، وأعد الاختبار.
- جدول التتبّع اليدوي يكشف الأخطاء المنطقية بالتفكير مثل الحاسب.
- نتيجة صحيحة مع مدخل واحد لا تثبت صحة البرنامج.
- جدول حالات الاختبار يقارن المتوقع بالفعلي قبل الإصلاح وبعده.
- الأسماء الواضحة والتنسيق والتحقق من المدخلات والبناء التدريجي تمنع كثيرًا من الأخطاء.
تحدٍّ برمجي: أصلح حاسبة المعدل
متقدمالبرنامج يقرأ عدد المواد ثم درجاتها ويطبع المعدل، لكنه يعطي نتيجة خاطئة، ويتوقف إذا كان عدد المواد صفرًا.
- استخدم جدول تتبّع أو الطباعة التشخيصية لإيجاد الخطأ المنطقي.
- أضف تحققًا يمنع القسمة على صفر ويطبع: لا توجد مواد
- مع المدخلات 3 ثم 80 90 70 يجب أن يطبع: المعدل = 80.0
ابدأ من هذا الكود، واضغط ▶ شغّل هنا لتكتب حلّك وتجرّبه مباشرة:
import java.util.Scanner;public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int n = in.nextInt(); int sum = 0; for (int i = 1; i < n; i++) { sum = sum + in.nextInt(); } double avg = sum / n; System.out.println("المعدل = " + avg); }}
💡 تلميح 1
💡 تلميح 2
حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
import java.util.Scanner;public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int n = in.nextInt(); if (n == 0) { System.out.println("لا توجد مواد"); return; } int sum = 0; for (int i = 1; i <= n; i++) { sum = sum + in.nextInt(); } double avg = (double) sum / n; System.out.println("المعدل = " + avg); }}