يا جماعة الخير، السلام عليكم ورحمة الله وبركاته. معكم أخوكم أبو عمر.
قبل كم سنة، كنت شغال في شركة على مشروع ضخم وحساس جدًا. كان في قلب النظام دالة (Function) الكل بخاف يقرب منها. كنا نسميها بين بعض “الوحش” أو “الصندوق الأسود”. هاي الدالة، اللي اسمها كان بريء جدًا `processTransaction()`، كانت عبارة عن كتلة واحدة من الكود تتجاوز الألف سطر. بتعمل كل إشي ممكن تتخيلوه: بتتحقق من المدخلات، بتتصل بالبنك، بتخصم من المخزون، بترسل إيميلات، بتسجل في عشرين ملف سجلات (Logs) مختلف، وحتى يمكن كانت بتحضر قهوة للموظفين، الله أعلم!
القصة وما فيها، في يوم من الأيام، طلب منا قسم الأعمال تعديل بسيط جدًا: “بدنا نضيف شرط جديد قبل إرسال إيميل التأكيد”. طلب منطقي وبسيط، صح؟ توجهت الأنظار للشاب الجديد في الفريق، خلينا نسميه “خالد”. مسكين خالد، أخذ المهمة بحماس، فتح ملف الكود، وشاف “الوحش” أمامه. قضى يومين كاملين وهو بحاول يفهم وين بالضبط لازم يحط السطرين الجداد بدون ما “يفجر” الدنيا. وجهه صار أصفر، والقهوة ما فارقت مكتبه.
بعد ما شفت اليأس في عيونه، حكيتله: “تعال يا خالد، خلينا نشرب فنجان قهوة ونحكي”. قلت للفريق كله: “يا جماعة، المشكلة مش في خالد ولا في التعديل، المشكلة في “الوحش” اللي ربيناه بإيدينا على مر السنين”. يومها، قررنا إنهاء هذه المهزلة، وكانت بداية رحلتنا مع عملية إنقاذ بسيطة لكن عظيمة اسمها “استخلاص الدالة” أو “Extract Method”.
ما هو “استخلاص الدالة” (Extract Method)؟
ببساطة شديدة، “استخلاص الدالة” هي عملية من عمليات إعادة الهيكلة (Refactoring). الفكرة إنك بتشوف قطعة من الكود داخل دالة كبيرة بتعمل شغلة محددة ومستقلة، فبتقوم “بقص” هاي القطعة وبتحطها في دالة جديدة لحالها، وبتعطيها اسم واضح ومعبّر عن وظيفتها. بعدين، في المكان الأصلي اللي أخذت منه الكود، بتستدعي الدالة الجديدة اللي عملتها.
الفكرة تبدو تافهة، لكن تأثيرها مثل السحر. بتحول الكود من طلاسم غير مفهومة إلى قصة واضحة ومقسمة لفصول، كل فصل (دالة) له عنوان ووظيفة محددة.
لماذا تنشأ هذه “الوحوش” البرمجية أصلًا؟
قبل ما نحكي عن الحل، لازم نفهم أصل المشكلة. هاي الدوال العملاقة ما بتنكتب هيك من أول يوم. هي بتنمو وبتكبر مع الزمن مثل كرة الثلج، ولأسباب كلنا بنعرفها:
التطور التدريجي وضغط الوقت
كل مرة بيجي طلب تعديل جديد، والمبرمج بيكون مستعجل، فبيضيف كم سطر `if-else` هنا، وشوية منطق هناك داخل نفس الدالة. “بس عشان تمشي الأمور الآن”، وبيوعد حاله إنه “بيرجع بنظفها بعدين”. وكلمة “بعدين” هاي عمرها ما بتيجي.
الخوف من التغيير
لما الدالة تكبر وتصير معقدة، بيصير الكل يخاف يعدّل عليها. شعارهم بصير: “إذا شغالة، لا تلمسها!” (If it works, don’t touch it). هذا الخوف بيخلي المشكلة تتفاقم، لأن كل تعديل جديد بيتم عن طريق “الترقيع” بدل الحل الجذري.
غياب الوعي بمبادئ الكود النظيف
بعض المبرمجين، خصوصًا في بداية مسيرتهم، ما بيكون عندهم وعي كافٍ بأهمية تقسيم الكود وتنظيمه. المهم عندهم إنه الكود “يشتغل”، بغض النظر عن كيف مكتوب.
رحلة الإنقاذ: تشريح الوحش خطوة بخطوة
نرجع لقصتنا مع دالة `processTransaction()`. قررنا نعمل ورشة عمل صغيرة، وكان هدفنا مش بس نصلح الكود، بل نعلم الفريق كله كيف يفكر بطريقة صحيحة. هذا مثال مبسط جدًا للكود قبل التعديل (تخيل إنه كل تعليق هو 50 سطر كود):
قبل إعادة الهيكلة (Before Refactoring):
def process_transaction(user, amount, items): # ============================================ # 1. التحقق من صحة بيانات المستخدم والطلب # ============================================ print("التحقق من بيانات المستخدم...") if not user.is_active: print("خطأ: المستخدم غير نشط") return {"status": "error", "message": "User not active"} if amount <= 0: print("خطأ: المبلغ غير صحيح") return {"status": "error", "message": "Invalid amount"} # ... المزيد من عمليات التحقق ... # ============================================ # 2. معالجة الدفع عبر بوابة الدفع # ============================================ print("الاتصال ببوابة الدفع...") payment_gateway = PaymentGateway() result = payment_gateway.charge(user.credit_card, amount) if not result.is_success: print("خطأ: عملية الدفع فشلت") return {"status": "error", "message": "Payment failed"} # ... المزيد من منطق الدفع ... # ============================================ # 3. تحديث المخزون # ============================================ print("تحديث المخزون...") for item in items: db.update_inventory(item.id, -1) # ... المزيد من منطق المخزون ... # ============================================ # 4. إرسال إيميل تأكيد للعميل # ============================================ print("إرسال إيميل...") email_service.send( to=user.email, subject="تمت عملية الشراء بنجاح", body=f"شكرًا لك على شراءك بمبلغ {amount}" ) # ... المزيد من منطق الإيميلات ... print("انتهت العملية بنجاح") return {"status": "success", "transaction_id": result.transaction_id}
الكود شغال، لكنه كارثة من ناحية الصيانة. أي تعديل في أي جزء يتطلب فهم كل الأجزاء الأخرى. الآن، لنرى كيف طبقنا سحر “استخلاص الدالة”.
الخطوة الأولى: استخلاص دالة التحقق (Validation)
أول جزء واضح هو كتلة التحقق من المدخلات. قصيناها وحطيناها في دالة جديدة اسمها `validate_transaction_data`. الاسم واضح ومباشر.
def validate_transaction_data(user, amount):
if not user.is_active:
raise ValueError("User not active")
if amount <= 0:
raise ValueError("Invalid amount")
# ... المزيد من عمليات التحقق ...
# الدالة الأصلية بعد التعديل الأول
def process_transaction(user, amount, items):
try:
validate_transaction_data(user, amount)
except ValueError as e:
print(f"خطأ في التحقق: {e}")
return {"status": "error", "message": str(e)}
# ... باقي الكود ...
لاحظ كيف أصبحت الدالة الرئيسية أبسط. الآن هي تقول: “أولًا، تحقق من البيانات”. كيف؟ هذا ليس من شأنها، بل من شأن الدالة الجديدة.
الخطوة الثانية: استخلاص باقي الكتل المنطقية
كررنا نفس العملية مع باقي الأجزاء: الدفع، تحديث المخزون، وإرسال الإيميل.
بعد إعادة الهيكلة (After Refactoring):
# دالة مساعدة 1: التحقق def validate_transaction_data(user, amount): if not user.is_active: raise ValueError("User not active") if amount <= 0: raise ValueError("Invalid amount") # دالة مساعدة 2: معالجة الدفع def charge_customer(user, amount): payment_gateway = PaymentGateway() result = payment_gateway.charge(user.credit_card, amount) if not result.is_success: raise PaymentError("Payment failed") return result.transaction_id # دالة مساعدة 3: تحديث المخزون def update_inventory_for_items(items): for item in items: db.update_inventory(item.id, -1) # دالة مساعدة 4: إرسال الإيميل def send_confirmation_email(user, amount): email_service.send( to=user.email, subject="تمت عملية الشراء بنجاح", body=f"شكرًا لك على شراءك بمبلغ {amount}" ) # ============================================ # الدالة الرئيسية: أصبحت قصة سهلة القراءة # ============================================ def process_transaction(user, amount, items): try: print("بدء معالجة العملية...") validate_transaction_data(user, amount) transaction_id = charge_customer(user, amount) update_inventory_for_items(items) send_confirmation_email(user, amount) print("انتهت العملية بنجاح") return {"status": "success", "transaction_id": transaction_id} except ValueError as e: print(f"خطأ في بيانات الطلب: {e}") return {"status": "error", "message": str(e)} except PaymentError as e: print(f"خطأ في الدفع: {e}") # هنا يمكن إضافة منطق لإلغاء العملية إذا فشل الدفع return {"status": "error", "message": str(e)} except Exception as e: print(f"حدث خطأ غير متوقع: {e}") # التعامل مع أي أخطاء أخرى return {"status": "error", "message": "An unexpected error occurred"}
انظر إلى جمال الدالة الرئيسية الآن! أصبحت مثل قائد الأوركسترا، لا يعزف على كل الآلات بنفسه، بل يوجه كل عازف (دالة) ليقوم بدوره في الوقت المناسب. الكود الآن يقرأ كأنه قصة: تحقق، ثم ادفع، ثم حدّث المخزون، ثم أرسل إيميلًا. كل خطوة معزولة في دالتها الخاصة، ويمكن تعديلها أو اختبارها بشكل مستقل.
نصائح أبو عمر الذهبية في فن “استخلاص الدالة”
من خبرتي في هذا المجال، اسمحولي أقدم لكم كم نصيحة عملية:
- الاسم هو كل شيء: اسم الدالة الجديدة يجب أن يكون معبرًا جدًا عن وظيفتها (فعل + اسم). لا تسميها `do_stuff()` أو `helper1()`. سمّها `calculate_shipping_cost()` أو `validate_user_input()`.
- لا تخف من الدوال الصغيرة: من الأخطاء الشائعة الخوف من كثرة الدوال. صدقني، وجود 20 دالة صغيرة كل واحدة من 5 أسطر، أفضل ألف مرة من دالة واحدة من 100 سطر.
- استخدم أدواتك بذكاء: معظم بيئات التطوير الحديثة (IDEs) مثل VS Code, IntelliJ, Visual Studio لديها أدوات مدمجة لعملية “Extract Method”. حدد الكود، اضغط بالزر اليمين، واختر “Refactor -> Extract Method”. هي بتعمل كل الشغل الصعب عنك.
- خطوة بخطوة: لا تحاول إعادة هيكلة دالة من 1000 سطر في جلسة واحدة. ابدأ بأوضح جزء، استخلصه، تأكد أن كل شيء ما زال يعمل (عن طريق الاختبارات الآلية)، ثم انتقل للجزء التالي.
- اجعلها عادة: أفضل طريقة لتجنب الوحوش هي قتلها وهي صغيرة. كلما كتبت بضعة أسطر وشعرت أنها تشكل كتلة منطقية، فكر فورًا: “هل يمكن أن تكون هذه دالة مستقلة؟”.
الخلاصة: ودّع الوحوش، ورحّب بالكود النظيف
في النهاية، “استخلاص الدالة” ليس مجرد تقنية برمجية، بل هو تغيير في طريقة التفكير. هو الانتقال من عقلية “أريد فقط أن يعمل الكود” إلى عقلية “أريد أن يعمل الكود ويكون مفهومًا وقابلًا للصيانة لي وللفريق من بعدي”.
تذكروا دائمًا أننا نقضي وقتًا في قراءة الكود أكثر بكثير من كتابته. استثمار القليل من الوقت في تنظيم الكود اليوم سيوفر عليك وعلى فريقك ساعات وأيام من العذاب في المستقبل.
فلا تتركوا الوحوش تنمو في مشاريعكم. حاربوها بتقنية “استخلاص الدالة”، واجعلوا الكود صديقكم، لا عدوكم. بالتوفيق يا جماعة! 👨💻✨