كانت دوالنا وحوشًا من ألف سطر: كيف أنقذنا ‘استخلاص الدالة’ (Extract Method) من جحيم التعقيد؟

يا جماعة الخير، السلام عليكم ورحمة الله وبركاته. معكم أخوكم أبو عمر.

قبل كم سنة، كنت شغال في شركة على مشروع ضخم وحساس جدًا. كان في قلب النظام دالة (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 سطر في جلسة واحدة. ابدأ بأوضح جزء، استخلصه، تأكد أن كل شيء ما زال يعمل (عن طريق الاختبارات الآلية)، ثم انتقل للجزء التالي.
  • اجعلها عادة: أفضل طريقة لتجنب الوحوش هي قتلها وهي صغيرة. كلما كتبت بضعة أسطر وشعرت أنها تشكل كتلة منطقية، فكر فورًا: “هل يمكن أن تكون هذه دالة مستقلة؟”.

الخلاصة: ودّع الوحوش، ورحّب بالكود النظيف

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

تذكروا دائمًا أننا نقضي وقتًا في قراءة الكود أكثر بكثير من كتابته. استثمار القليل من الوقت في تنظيم الكود اليوم سيوفر عليك وعلى فريقك ساعات وأيام من العذاب في المستقبل.

فلا تتركوا الوحوش تنمو في مشاريعكم. حاربوها بتقنية “استخلاص الدالة”، واجعلوا الكود صديقكم، لا عدوكم. بالتوفيق يا جماعة! 👨‍💻✨

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

أتمتة العمليات

كانت تقاريرنا اليومية تستهلك ساعات: كيف أنقذتنا ‘أتمتة العمليات الروبوتية’ (RPA) من جحيم النقرات المتكررة؟

قصتي الشخصية مع أتمتة التقارير اليومية التي كانت تسرق ساعات من وقت فريقنا. اكتشفوا معنا ما هي أتمتة العمليات الروبوتية (RPA)، وكيف يمكنها أن تحرركم...

28 مايو، 2026 قراءة المزيد
​معمارية البرمجيات

كان تحديث نظامنا القديم كابوساً: كيف أنقذنا نمط ‘التين الخانق’ من جحيم ‘إعادة البناء الكبرى’؟

أذكر جيداً ذلك الاجتماع الذي كاد أن يودي بمستقبل مشروعنا. بدلاً من "إعادة البناء الكبرى" المحفوفة بالمخاطر، لجأنا إلى نمط "التين الخانق" (Strangler Fig) لترحيل...

28 مايو، 2026 قراءة المزيد
ذكاء اصطناعي

نماذجنا اللغوية كانت تهذي! كيف أنقذنا الذكاء الاصطناعي من الهلوسة بتقنية RAG؟

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

28 مايو، 2026 قراءة المزيد
تسويق رقمي

كانت حملاتنا تصرخ في الفراغ: كيف أنقذتنا ‘التجزئة الديناميكية بالتعلم الآلي’ من جحيم العروض غير الملائمة؟

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

28 مايو، 2026 قراءة المزيد
تجربة المستخدم والابداع البصري

كان تطبيقنا يستبعد 15% من المستخدمين بصمت: كيف أنقذتنا ‘إمكانية الوصول الرقمية’ (a11y) من كارثة التصميم؟

كنا نظن أن تطبيقنا ناجح، حتى اكتشفنا أننا كنا نستبعد 15% من مستخدمينا بصمت. هذه قصتي كـ 'أبو عمر' وكيف غيرت 'إمكانية الوصول الرقمية' (a11y)...

28 مايو، 2026 قراءة المزيد
برمجة وقواعد بيانات

ما وراء البحث التقليدي: كيف تُمكّن قواعد بيانات المتجهات تطبيقات الذكاء الاصطناعي من فهم المعنى؟

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

27 مايو، 2026 قراءة المزيد
البودكاست