مقدمة: لماذا يتطلب الكود القديم بلغة Go إعادة هيكلة؟
تشتهر Go (Golang) ببساطتها وأدائها. ومع ذلك، حتى في المشاريع المكتوبة بلغة Go، يتراكم مع الوقت الكود القديم — الإنشاءات القديمة، الدوال غير القابلة للقراءة، الأرقام "السحرية"، وغياب الاختبارات. يصبح العمل مع هذا الكود كابوسًا: كل تغيير محفوف بالمخاطر، وإضافة وظائف جديدة تستغرق أسابيع.
إعادة الهيكلة ليست إعادة كتابة المشروع من الصفر. إنها إعادة تنظيم آمنة للهيكل الداخلي للكود دون تغيير سلوكه الخارجي. في هذه المقالة، سنشرح خطة خطوة بخطوة لإعادة هيكلة الكود القديم بلغة Go، من التحليل إلى التنفيذ. ستتعلم كيفية تحويل "كود السباغيتي" المتشابك إلى مشروع نظيف وقابل للصيانة والاختبار.
المرحلة 1: التدقيق ورسم خريطة الكود القديم
قبل تغيير الكود، يجب أن تفهم ما تتعامل معه. تخطي هذه المرحلة — وستخاطر بكسر منطق لا يتذكره أحد.
1.1. البحث عن "الأعلام الحمراء"
ابدأ بالبحث عن الأنماط المعادية النموذجية في Go:
- دوال God — دوال يزيد طولها عن 100 سطر.
- المتغيرات العامة — ارتباط قوي وصعوبة في الاختبار.
- الأرقام/النصوص السحرية — الثوابت المتناثرة في الكود.
- غياب معالجة الأخطاء — تجاهل
errorالمرتجع. - استخدام panic بدلاً من الأخطاء — استخدام
panic/recoverلمنطق الأعمال. - خلط الطبقات — استعلامات SQL داخل معالجات HTTP.
1.2. إنشاء خريطة التبعيات
استخدم أدوات مثل go callgraph أو guru لتصور استدعاءات الدوال. سيساعدك هذا في تحديد:
- التبعيات الدائرية بين الحزم.
- الكود "الميت" الذي لا يتم استدعاؤه في أي مكان.
- المسارات الحرجة التي لا يمكن لمسها دون تغطية مسبقة بالاختبارات.
المرحلة 2: إنشاء "شبكة أمان" من الاختبارات
إعادة الهيكلة بدون اختبارات هي قفزة بدون حبل أمان. مهمتك هي كتابة اختبارات مميزة تثبت السلوك الحالي للنظام، حتى لو بدا غير صحيح.
2.1. الاختبار على مستوى الدوال
ابدأ بأكثر أجزاء الكود أهمية وعزلة. مثال لدالة يُخطط لإعادة هيكلتها:
// كود قديمfunc CalculatePrice(base float64, discountCode string) float64 { if discountCode == "SALE10" { return base * 0.9 } if discountCode == "VIP20" { return base * 0.8 } return base}
// اختبار مميزfunc TestCalculatePrice(t *testing.T) { tests := []struct { name string base float64 code string want float64 }{ {"بدون خصم", 100.0, "", 100.0}, {"خصم 10%", 100.0, "SALE10", 90.0}, {"خصم 20%", 100.0, "VIP20", 80.0}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := CalculatePrice(tt.base, tt.code) if got != tt.want { t.Errorf("CalculatePrice(%v, %v) = %v; want %v", tt.base, tt.code, got, tt.want) } }) }}2.2. اختبارات التكامل باستخدام الملفات الذهبية
للهياكل المعقدة (استجابات JSON، إنشاء التقارير)، استخدم تقنية "الملفات الذهبية". سجل المخرجات الحالية في ملف، وعند إعادة الهيكلة، قارنها به.
func TestHandler(t *testing.T) { // تنفيذ الطلب req := httptest.NewRequest("GET", "/api/data", nil) rec := httptest.NewRecorder() myHandler(rec, req)
// المقارنة مع المرجع expected, _ := os.ReadFile("testdata/golden.json") if !bytes.Equal(rec.Body.Bytes(), expected) { t.Errorf("Output mismatch") }\