الكود النظيف وإعادة الهيكلة
درس 3 من 3
3الكود النظيف وإعادة الهيكلة
الصورة الكبيرةالكود النظيف ليس ذوقًا شخصيًا. هو كود يفهمه زميلك (أو أنت بعد ستة أشهر) بسرعة، فيعدّله دون خوف. وإعادة الهيكلة تغيير الشكل دون تغيير السلوك.
ماذا تشاهد في الرسم: إعادة الهيكلة الآمنة: خطوات صغيرة، والاختبارات تُشغَّل بعد كل خطوة.
خارطة هذا الجزء
المفاهيم بالترتيب، اضغط أيًّا منها للقفز إليه:
طريقان إلى نفس الفكرة
مثل ترتيب مطبخ: لا تغيّر الوصفات، بل تضع البهارات معًا، وتكتب أسماء على العلب، وترمي ما لا يُستخدم. الطبخ بعدها أسرع وأقل أخطاء. وترتّب رفًا واحدًا كل مرة، لا المطبخ كله في يوم.
مبادئ عملية: الأسماء تكشف النية (days_until_expiry لا d)، الدالة تفعل شيئًا واحدًا على مستوى تجريد واحد، العودة المبكرة (guard clauses) بدل التداخل العميق، الثوابت المسمّاة بدل الأرقام السحرية، DRY بحذر (تكرار مرتين أرخص من تجريد خاطئ)، وفصل الحساب النقي عن الآثار الجانبية (I/O). روائح الكود: دالة طويلة، قائمة وسطاء طويلة، تعليقات تشرح ما يجب أن يقوله الاسم. إعادة الهيكلة (Fowler): Extract Function، Rename، Replace Magic Number، Introduce Parameter Object، كل خطوة صغيرة وتُشغَّل الاختبارات بعدها، ويُفصل التغيير الشكلي عن السلوكي في commits مختلفة.
قبل أن تبدأ
🧪الاختبارات الوحدوية
درس الاختبارات في هذا المسار.
الفكرة الجوهرية
1قبل وبعد
دالة حقيقية الشكل: أسماء غامضة، أرقام سحرية، وتداخل عميق. نعيد هيكلتها خطوة خطوة ونتأكد أن النتائج لم تتغير:
def calc(o):
t = 0
if o:
if o["items"]:
for i in o["items"]:
t += i["p"] * i["q"]
if o["member"]:
t = t * 0.9
if t > 200:
t = t - 15
return round(t, 2)
MEMBER_DISCOUNT = 0.10
BIG_ORDER_THRESHOLD = 200
BIG_ORDER_REWARD = 15
def subtotal(items):
return sum(item["p"] * item["q"] for item in items)
def order_total(order):
if not order or not order["items"]:
return 0
total = subtotal(order["items"])
if order["member"]:
total *= 1 - MEMBER_DISCOUNT
if total > BIG_ORDER_THRESHOLD:
total -= BIG_ORDER_REWARD
return round(total, 2)
cases = [None, {"items": [], "member": False},
{"items": [{"p": 14, "q": 2}], "member": True},
{"items": [{"p": 50, "q": 5}], "member": False}]
for case in cases:
assert calc(case) == order_total(case)
print("same behaviour on", len(cases), "cases:", [order_total(c) for c in cases])2الأسماء والدوال الصغيرة
الاسم الجيد يغني عن التعليق. لو احتجت تعليقًا يشرح ما تفعله كتلة كود، فاستخرجها لدالة اسمها هو ذلك التعليق. والدالة التي تحتاج كلمة «و» لوصفها («تحسب وترسل») تفعل شيئين.
3التكرار والتجريد
قاعدة الثلاثة: أول مرة اكتب، ثاني مرة لاحظ التكرار، ثالث مرة استخرج. التجريد المبكر يربط أشياء متشابهة صدفة، ثم تتباعد فيمتلئ بالشروط.
مثال محلول
المسألة: if user: if user.active: if user.email: send(...). ثلاث مستويات تداخل. إعادة الهيكلة: if not user or not user.active or not user.email: return ثم send(...). التحقق: نفس الاختبارات الثلاثة (مستخدم None، غير نشط، بلا بريد) تمر. الكود صار سطرين يُقرآن من الأعلى للأسفل.
خرافة مقابل حقيقة
| الخرافة | الحقيقة |
|---|---|
| الكود النظيف = كود قصير. | سطر واحد ذكي غامض أسوأ من خمسة أسطر واضحة. |
| إعادة الهيكلة تحتاج أسبوعًا مخصصًا. | أفضلها صغير ومستمر: اترك الملف أنظف قليلًا مما وجدته. |
اختبر فهمك
أي اسم أفضل لمتغير يحمل عدد الأيام حتى انتهاء الاشتراك؟
الاسم يقول ماذا يحمل وبأي وحدة.
ما شرط إعادة الهيكلة الآمنة؟
بلا اختبارات لا تعرف إن غيّرت السلوك.
متى تستخرج كودًا مكررًا لدالة؟
قاعدة الثلاثة تتجنّب التجريد المبكر الخاطئ.
بطاقات مراجعة سريعة
تحقق من فهمك
لماذا نفصل commit إعادة الهيكلة عن الميزة؟
ليُراجع كل واحد بسهولة، ويُعكس وحده إن لزم.
دالة اسمها process_data. ما المشكلة؟
الاسم لا يقول شيئًا. سمّها بما تفعله فعلًا.
متى يكون التعليق مفيدًا؟
حين يشرح لماذا (قرار أو قيد خارجي)، لا ماذا يفعل الكود.
مصادر هذا الدرس
مرتّبة بالنوع. ابدأ بالتوثيق الرسمي إذا تبي المرجع الدقيق، وبالمقال أو الفيديو إذا تبي الشرح أولًا.
توثيق رسمي2
خلّصت الدرس؟
سجّل الدخول ليُحفظ تقدّمك.