يا جماعة الخير، السلام عليكم ورحمة الله وبركاته. معكم أخوكم أبو عمر.
قبل كم سنة، مسكت مشروع صيانة لنظام قديم شوي. كان الكود المكتوب فيه، زي ما بنحكيها بالعامية، “مكركب فوق بعضه”. وفي يوم من الأيام، ظهرت مشكلة عويصة في جزئية معالجة طلبات المستخدمين. كانت الدالة (function) المسؤولة عن هالقصّة عبارة عن تحفة فنية من التعقيد… بس بالمعنى السلبي للكلمة!
أتذكر أني فتحت الملف، ولقيت قدامي دالة اسمها processRequest. يا لطيف! كانت عبارة عن شجرة من جمل if/else متداخلة بشكل هرمي، لدرجة إني حسيت حالي دخلت متاهة ومش راح أطلع منها. كل شرط بيفتح فرع جديد، والفرع بيفتح فروع، وهيك… كنا بنسميها “شفرة المعكرونة” (Spaghetti Code) من كثر ما هي متشابكة. عشان أتبع مسار منطقي واحد، كنت بحاجة لثلاث شاشات وكمية قهوة مش طبيعية. والمصيبة الأكبر، لما كنت أعدّل سطر في مكان، كانت تضرب الدنيا في مكان ثاني ما إله علاقة. والله غلبتني!
بعد ساعات من التحديق في الشاشة ومحاولة رسم تدفق الكود على الورق، وقفت لحظة وحكيت لحالي: “شو هالحكي يا أبو عمر؟ أكيد في طريقة أحسن”. وهنا، تذكرت مبدأ بسيط لكن فعّال جدًا قرأت عنه في كتاب “الكود النظيف” (Clean Code): جمل الحراسة أو الـ Guard Clauses. كانت تلك اللحظة بمثابة ضوء في نهاية النفق. مسكت الكود وبدأت بتفكيكه قطعة قطعة، محوّلاً تلك المتاهة الهرمية إلى طريق مستقيم وواضح. في هذه المقالة، سآخذكم في نفس الرحلة وأريكم كيف يمكن لهذا المفهوم أن ينقذكم أنتم أيضًا.
ما هو جحيم الشروط المتداخلة (The Pyramid of Doom)؟
قبل أن نتحدث عن الحل، دعونا نُشخّص المشكلة بدقة. “جحيم الشروط المتداخلة” أو ما يُعرف بـ “هرم الموت” (Pyramid of Doom) هو الاسم الذي يُطلق على الشفرة البرمجية التي تحتوي على بنى if/else متداخلة بعمق. كلما زاد العمق، زادت الصعوبة في:
- القراءة والفهم: العقل البشري يجد صعوبة في تتبع مسارات متعددة ومتشعبة في نفس الوقت. يصبح من الصعب معرفة الشروط التي يجب أن تتحقق للوصول إلى جزء معين من الكود.
- الصيانة والتعديل: أي تغيير بسيط قد يتطلب تعديل المنطق في عدة أماكن، مما يزيد من احتمالية حدوث أخطاء جانبية غير متوقعة.
- التصحيح (Debugging): عندما يحدث خطأ، يصبح تحديد السبب كالبحث عن إبرة في كومة قش. أي فرع من هذه الشجرة هو الذي تسبب في المشكلة؟
مثال على الكود المتشابك
لنتخيل أننا نكتب دالة للتحقق من صلاحية مستخدم لإجراء عملية شراء. قد يبدو الكود المبدئي هكذا (باستخدام JavaScript كمثال):
function processPayment(user, order) {
if (user) {
if (user.isVerified) {
if (order) {
if (order.items.length > 0) {
if (order.totalAmount < user.balance) {
// ----- المسار السعيد (Happy Path) -----
// كل الشروط تحققت، هنا يتم تنفيذ المنطق الرئيسي
console.log("Processing payment...");
// ... المزيد من الكود لمعالجة الدفع
return { success: true, message: "Payment successful!" };
// ------------------------------------
} else {
return { success: false, message: "Insufficient balance." };
}
} else {
return { success: false, message: "Order cannot be empty." };
}
} else {
return { success: false, message: "Invalid order." };
}
} else {
return { success: false, message: "User is not verified." };
}
} else {
return { success: false, message: "Invalid user." };
}
}
لاحظوا كيف أن المنطق الرئيسي المهم (معالجة الدفع) مدفون في أعماق الكود. هذا هو بالضبط ما نريد تجنبه.
المنقذ: جمل الحراسة (Guard Clauses)
ببساطة، جمل الحراسة هي تقنية برمجية تعتمد على قلب المنطق. بدلاً من التحقق من “المسار السعيد” (Happy Path)، نقوم بالتحقق من الحالات الاستثنائية أو “المسارات غير السعيدة” (Unhappy Paths) في بداية الدالة والخروج منها مبكرًا.
فكر فيها كحارس أمن على باب مبنى. الحارس لا يسأل كل شخص “هل أنت موظف؟” ثم يدخله إلى الداخل ليفحص بطاقته. بل العكس، هو يسأل “هل لديك بطاقة؟”. إذا لم تكن لديك، يمنعك من الدخول فورًا. أما إذا كانت لديك، فتكمل طريقك بسهولة. جمل الحراسة تفعل نفس الشيء مع الكود الخاص بك.
إعادة هيكلة الكود باستخدام جمل الحراسة
دعونا الآن نأخذ نفس المثال السابق ونعيد كتابته باستخدام هذا المبدأ الرائع.
function processPaymentWithGuards(user, order) {
// جملة الحراسة الأولى: التحقق من وجود المستخدم
if (!user) {
return { success: false, message: "Invalid user." };
}
// جملة الحراسة الثانية: التحقق من أن المستخدم موثّق
if (!user.isVerified) {
return { success: false, message: "User is not verified." };
}
// جملة الحراسة الثالثة: التحقق من وجود الطلب
if (!order) {
return { success: false, message: "Invalid order." };
}
// جملة الحراسة الرابعة: التحقق من أن الطلب ليس فارغًا
if (order.items.length === 0) {
return { success: false, message: "Order cannot be empty." };
}
// جملة الحراسة الخامسة: التحقق من الرصيد الكافي
if (order.totalAmount >= user.balance) {
return { success: false, message: "Insufficient balance." };
}
// ----- المسار السعيد (Happy Path) -----
// إذا وصلنا إلى هنا، فهذا يعني أن كل شيء سليم 100%
// الكود الرئيسي واضح ومباشر وبدون أي تداخل
console.log("Processing payment...");
// ... المزيد من الكود لمعالجة الدفع
return { success: true, message: "Payment successful!" };
}
هل رأيتم الفرق؟ الكود أصبح مسطحًا (flat) وسهل القراءة. المنطق الرئيسي لم يعد مدفونًا في الأعماق، بل أصبح واضحًا في نهاية الدالة. أي مبرمج يقرأ هذا الكود سيفهم فورًا ما هي الشروط المسبقة وما هو الهدف الرئيسي للدالة.
نصائح من خبرة أبو عمر
على مدار سنوات عملي، تعلمت بعض الدروس حول استخدام جمل الحراسة بفعالية. اسمحوا لي بمشاركتها معكم:
1. استخدمها للتحقق من الشروط المسبقة (Pre-conditions)
جمل الحراسة تتألق عندما تستخدمها للتحقق من صحة المدخلات (parameters) أو الظروف الأساسية اللازمة لعمل الدالة. هل الكائن (object) ليس null؟ هل المستخدم قام بتسجيل الدخول؟ هل القائمة ليست فارغة؟ هذه كلها مرشحة مثالية لجمل الحراسة.
2. الخروج المبكر هو صديقك
لا تخف من استخدام return، throw، أو حتى continue (داخل الحلقات التكرارية) للخروج من السياق الحالي. الهدف هو التخلص من الحالات الشاذة بأسرع ما يمكن للتركيز على المهمة الأساسية.
3. اجعل رسائل الخطأ ذات معنى
عندما تخرج مبكرًا بسبب خطأ ما، تأكد من أن القيمة المُرجعة أو رسالة الخطأ (exception message) واضحة ومفيدة. هذا يساعد بشكل كبير في عملية تصحيح الأخطاء لاحقًا. بدلًا من إرجاع false فقط، أرجع كائنًا يصف المشكلة كما فعلنا في المثال.
4. لا تفرط في استخدامها للمنطق المعقد
في بعض الأحيان، يكون لديك منطق أعمال (business logic) معقد يتطلب بالفعل بنية if/else if/else. جمل الحراسة ليست حلاً سحريًا لكل شيء. هي الأفضل للتحقق من الصلاحيات والحالات الاستثنائية، وليس لاستبدال كل بنية شرطية في الكود.
الخلاصة: كود أنظف، حياة أسهل 😉
في عالم البرمجة، التعقيد هو العدو الأول. كلما كان الكود أبسط وأوضح، كان أسهل في الصيانة والتطوير وأقل عرضة للأخطاء. جمل الحراسة (Guard Clauses) هي واحدة من أقوى الأدوات في ترسانة المبرمج لتحقيق هذه البساطة.
تذكروا دائمًا:
- تخلص من الحالات الشاذة أولاً: ابدأ دالتك بالتحقق من كل ما يمكن أن يسبب فشلها واخرج مبكرًا.
- اجعل المسار الرئيسي واضحًا: بعد التخلص من الحالات الشاذة، يجب أن يكون الكود المتبقي هو المسار المنطقي الرئيسي للدالة، مستقيمًا وسهل المتابعة.
في المرة القادمة التي تجد فيها نفسك غارقًا في شروط متداخلة، تذكر قصة أبو عمر وهذه التقنية البسيطة. طبقها، وستشعر بالفرق فورًا في راحة بالك وصفاء ذهنك أثناء البرمجة.
أتمنى لكم كتابة شفرات برمجية نظيفة وواضحة دائمًا. دمتم بخير يا جماعة!