ربما أكتب عن شيء واضح للكثيرين، ولكن ربما ليس للجميع. أعتقد أن إعادة الهيكلة موضوع معقد لأنه ينطوي على تغيير الكود دون التأثير على تشغيله.
ولذلك، فإنه من غير المفهوم للبعض أن إعادة الهيكلة هو في الواقع مجال من مجالات البرمجة، وهو أيضًا جزء مهم جدًا من عمل المبرمج. فالكود البرمجي يتطور باستمرار، وسيتم تعديله طالما أن هناك إمكانية لإضافة وظائف جديدة. ومع ذلك، قد تأخذ شكلاً لم يعد يسمح بإضافة وظائف جديدة بشكل فعال، وسيكون من الأسهل إعادة كتابة البرنامج بأكمله.
ما هي إعادة الهيكلة؟
عادةً ما تكون الإجابة التي تسمعها هي أنها تغيير بنية الشيفرة البرمجية عن طريق تطبيق سلسلة من تحويلات إعادة الهيكلة دون التأثير على السلوك الملحوظ للشيفرة البرمجية. وهذا صحيح. لقد صادفت مؤخرًا تعريفًا من قبل مارتن فاولر في كتابه "تحسين تصميم المدونة الحالية" حيث يصف إعادة الهيكلة "إجراء تغييرات كبيرة بخطوات صغيرة." ويصف إعادة الهيكلة كتغيير في التعليمات البرمجية لا يؤثر على عملها، لكنه يؤكد على ضرورة أن يتم ذلك بخطوات صغيرة.
ويدعو الكتاب أيضًا إلى ما يلي إعادة الهيكلة لا يؤثر على تشغيل الكود ويشير إلى أنه ليس له أي تأثير على اجتياز الاختبارات في أي وقت. يصف خطوة بخطوة كيفية إجراء إعادة الهيكلة. أعجبني كتابه لأنه يصف حيلًا بسيطة يمكن استخدامها في العمل اليومي.
لماذا نحتاج إلى إعادة الهيكلة؟
في معظم الأحيان، قد تحتاج إليها عندما تريد إدخال وظيفة جديدة ولا تسمح الشيفرة في نسختها الحالية بذلك أو سيكون الأمر أكثر صعوبة دون إجراء تغييرات على الشيفرة. أيضًا، يكون مفيدًا في الحالات التي تكون فيها إضافة المزيد من الميزات غير مربحة من حيث الوقت، أي أنه سيكون من الأسرع إعادة كتابة الشيفرة من الصفر. أعتقد أنه يُنسى أحيانًا أن إعادة الهيكلة يمكن أن يجعل الشيفرة البرمجية أنظف وأكثر قابلية للقراءة. يكتب مارتن في كتابه كيف يقوم بإعادة الهيكلة عندما يشعر برائحة كريهة في الشيفرة، وكيف يضعها, "يترك دائماً مجالاً للأفضل". وقد فاجأني هنا برؤية إعادة الهيكلة كعنصر من عناصر العمل اليومي على الأكواد البرمجية. بالنسبة لي، غالبًا ما تكون الأكواد غير مفهومة بالنسبة لي، وقراءتها هي تجربة غير مفهومة لأن الأكواد البرمجية غالبًا ما تكون غير بديهية.
إن السمة المميزة للبرنامج المصمم بشكل جيد هي النمطيةوالتي بفضلها يكفي معرفة جزء صغير فقط من الكود لإدخال معظم التعديلات. كما أن النمطية تجعل من السهل على الأشخاص الجدد الدخول والبدء في العمل بكفاءة أكبر. ولتحقيق هذه النمطية، يجب تجميع عناصر البرنامج ذات الصلة معًا، بحيث تكون الروابط مفهومة ويسهل العثور عليها. لا توجد قاعدة عامة واحدة حول كيفية القيام بذلك. كلما عرفت وفهمت أكثر فأكثر كيف من المفترض أن تعمل الشيفرة البرمجية بشكل أفضل، يمكنك تجميع العناصر، ولكن في بعض الأحيان عليك أيضًا الاختبار والتحقق.
إحدى قواعد إعادة البناء في YAGNIوهي اختصار لعبارة "لن تحتاج إليها" وهي مشتقة من البرمجة الإلكترونية (XP) تُستخدم بشكل أساسي في المرونةتطوير البرمجيات الفرق. قصة طويلة باختصار, YAGNI يقول أنه يجب القيام بالأشياء الحديثة فقط. وهذا يعني في الأساس أنه حتى لو كانت هناك حاجة إلى شيء ما في المستقبل، فلا ينبغي القيام به الآن. لكن لا يمكننا أيضًا سحق المزيد من الإضافات وهنا تصبح النمطية مهمة.
عند الحديث عن إعادة الهيكلة، يجب ذكر أحد أهم العناصر الأساسية، أي الاختبارات. في إعادة الهيكلة، نحتاج إلى معرفة أن الكود لا يزال يعمل، لأن إعادة الهيكلة لا يغير طريقة عملها، بل هيكلها، لذا يجب اجتياز جميع الاختبارات. من الأفضل إجراء اختبارات لجزء من الشيفرة التي نعمل عليها بعد كل عملية تحويل صغيرة. فهذا يعطينا تأكيدًا على أن كل شيء يعمل كما ينبغي ويختصر وقت العملية بأكملها. هذا ما يتحدث عنه مارتن في كتابه - قم بإجراء الاختبارات قدر الإمكان حتى لا تتراجع خطوة إلى الوراء وتضيع الوقت في البحث عن تحويل أفسد شيئًا ما.
إعادة هيكلة التعليمات البرمجية بدون اختبار هو أمر مؤلم وهناك احتمال كبير أن يحدث خطأ ما. إذا كان ذلك ممكنًا، سيكون من الأفضل إضافة بعض الاختبارات الأساسية على الأقل التي ستعطينا القليل من الاطمئنان إلى أن الكود يعمل.
التحويلات المدرجة أدناه هي مجرد أمثلة لكنها مفيدة حقًا في البرمجة اليومية:
استخراج الدالة واستخراج المتغيرات - إذا كانت الدالة طويلة جدًا، تحقق مما إذا كانت هناك أي دوال ثانوية يمكن استخراجها. الأمر نفسه ينطبق على الأسطر الطويلة. يمكن أن تساعد هذه التحويلات في العثور على التكرارات في الشيفرة. بفضل الدوال الصغيرة، تصبح الشيفرة أكثر وضوحًا وفهمًا,
إعادة تسمية الدوال والمتغيرات - استخدام اصطلاح التسمية الصحيح أمر ضروري للبرمجة الجيدة. يمكن لأسماء المتغيرات، عندما يتم اختيارها بشكل جيد، أن تخبرنا بالكثير عن الكود,
تجميع الدوال في فئة - هذا التغيير مفيد عندما تقوم فئتان بإجراء عمليات متشابهة حيث يمكن أن يختصر طول الفئة,
تجاوز العبارة المتداخلة - إذا تحقق الشرط لحالة خاصة، قم بإصدار بيان إرجاع عند حدوث الشرط. غالبًا ما يشار إلى هذه الأنواع من الاختبارات باسم شرط الحراسة. يؤدي استبدال العبارة الشرطية المتداخلة ببيان خروج إلى تغيير التركيز في الشيفرة البرمجية. تعيّن بنية إن-إذا-إلا وزناً متساوياً لكلا المتغيرين. بالنسبة للشخص الذي يقرأ الشيفرة، فهي رسالة للشخص الذي يقرأ الشيفرة، بأن كل منهما متساوية في الاحتمال والأهمية,
إدخال حالة خاصة - إذا كنت تستخدم بعض الشروط في شيفرتك عدة مرات، فقد يكون من المفيد إنشاء بنية منفصلة لها. نتيجة لذلك، يمكن استبدال معظم عمليات التحقق من الحالات الخاصة باستدعاءات دالة بسيطة. غالبًا ما تكون القيمة الشائعة التي تتطلب معالجة خاصة هي القيمة الفارغة. لذلك، كثيرًا ما يُطلق على هذا النمط اسم الكائن الصفري. ومع ذلك، يمكن استخدام هذا النمط في أي حالة خاصة,
استبدال تعدد أشكال التعليمات الشرطية.
مثال على ذلك
هذا مقال عن إعادة الهيكلة وهناك حاجة إلى مثال. أريد أن أعرض نموذج إعادة هيكلة بسيط أدناه باستخدام تجاوز البيان المتداخل و استبدال تعدد أشكال التعليمات الشرطية. لنفترض أن لدينا دالة برنامج تقوم بإرجاع تجزئة بمعلومات عن كيفية سقي النباتات في الحياة الواقعية. من المحتمل أن تكون هذه المعلومات في النموذج، لكن في هذا المثال لدينا هذه المعلومات في الدالة.
تعريف سقي_المعلومات(النبات)
النتيجة = {}
إذا كان plant.is_a? Suculent || plant.is_a? صبار
النتيجة = { { كمية_الماء: "قليلاً"، كيف_إلى: "من القاع"، مدة_السقي: "2 أسابيع"}
elsif plant.is_a? Alocasia || plant.is_a? مارانتا
النتيجة = { كمية_الماء: "كمية كبيرة"، كيف_إلى: "كما تفضل"، مدة_الري: "5 أيام"}
elsif plant.is_a? بيبيروميا
النتيجة = { كمية_الماء: "Dicent amount",
كيف_إلى: "من الأسفل! لا يحبون الماء على الأوراق",
مدة_الري: "1 أسبوع"}
غير ذلك
النتيجة = { كمية_الماء: "الكمية المحددة",
كيف_إلى: "كما تفضل",
مدة_السقي: "1 أسبوع"
}
نهاية
إرجاع النتيجة
النهاية
الفكرة هي التغيير في حال العودة:
إذا كان plant.isa? Suculent || plant.isa? صبار
النتيجة = { كمية الماء: "قليلاً"، كيف: "من الأسفل",
إلى
إرجاع { كمية_الماء: "قليلاً"، كيف_إلى: "من الأسفل"، مدة_السقي: "2 أسابيع"} إذا كان plant.is_a? Suculent || plant.is_a? صبار
إرجاع {ماءالكمية: "قليلاً"، كيفلـ "من القاع"، سقيالمدة: "2 أسابيع" } إذا كان النباتa? الصبار || plant.is_a? صبار
وهكذا مع كل شيء، حتى نصل إلى دالة تبدو هكذا:
تعريف سقي_المعلومات(النبات)
إرجاع النتيجة = { كمية الماء: "قليلاً"، كيف: "من القاع"، مدة الري: "2 أسابيع"} إذا كان plant.isa? Suculent || plant.is_a? صبار
إرجاع النتيجة = { كمية الماء: "كمية كبيرة"، كيف: "كما تفضل"، مدة الري: "5 أيام"} إذا كان plant.isa? Alocasia || plant.is_a? مارانتا
إرجاع النتيجة = { كمية_الماء: "Dicent amount",
كيف "من الأسفل! لا يحبون الماء على الأوراق",
مدة الري: "1 أسبوع" } إذا كان plant.is_a؟ بيبيروميا
إرجاع النتيجة = { كمية_الماء: "Dicent amount",
كيف_إلى: "كما تفضل",
مدة_الري: "1 أسبوع"
}
النهاية
في النهاية، حصلنا بالفعل على نتيجة العودة. والعادة الجيدة هي القيام بذلك خطوة بخطوة واختبار كل تغيير. يمكنك استبدال كتلة if هذه بحالة تبديل وستبدو أفضل على الفور، ولن تضطر إلى التحقق من جميع حالات ifs في كل مرة. سيبدو الأمر هكذا:
تعريف سقي_المعلومات(النبات)
سويتش plant.class.to_string
حالة الصبار، الصبار
{مقدار الماء: "قليلاً"، كيف: "من القاع"، مدة_السقي: "2 أسابيع" }
حالة ألوكاسيا، مارانتا
{ كمية الماء: "كمية كبيرة"، كيف "كما تفضل"، مدة_الري: "5 أيام" }
حالة بيبيروميا
{ كمية_الماء: "كمية المياه",
كيف_إلى: "من الأسفل! لا يحبون الماء على الأوراق",
مدة_الري: "1 أسبوع" }
غير ذلك
{ كمية_الماء: "كمية_المياه",
كيف_إلى: "كما تفضل",
مدة_الري: "1 أسبوع" }
النهاية
النهاية
وبعد ذلك يمكنك تطبيق استبدال تعدد أشكال التعليمات الشرطية. هذا لإنشاء فئة ذات دالة تُرجع القيمة الصحيحة وتبدلها في أماكنها الصحيحة.
فئة سوكولنت
...
تعريف سقي_المعلومات()
إرجاع { كمية الماء: "قليلاً"، كيف: "من الأسفل"، مدة_الري: "2 أسابيع" }
النهاية
النهاية
فئة الصبار
...
تعريف سقي_المعلومات()
إرجاع { كمية الماء: "قليلاً"، كيف: "من الأسفل"، مدة_الري: "2 أسابيع" }
النهاية
النهاية
فئة ألوكاسيا
...
تعريف سقي_المعلومات
إرجاع { wateramountount الماء: "كمية كبيرة"، كيف: "كما تفضل"، مدة_الري: "5 أيام" }
النهاية
النهاية
فئة مارانتا
...
تعريف سقي_المعلومات
إرجاع { wateramountount الماء: "كمية كبيرة"، كيف: "كما تفضل"، مدة_الري: "5 أيام" }
النهاية
النهاية
فئة بيبيروميا
...
تعريف سقي_المعلومات
إرجاع { water_amount: "Dicent amount",
كيف_إلى: "من الأسفل! لا يحبون الماء على الأوراق",
مدة_الري: "1 أسبوع" }
النهاية
النهاية
فئة النبات
...
تعريف سقي_المعلومات
إرجاع { water_amount: "Dicent amount",
كيف_إلى: "كما تفضل",
مدة_الري: "1 أسبوع" }
النهاية
النهاية
وفي دالة_السقي_الرئيسية، سيبدو الرمز كالتالي:
تعريف سقي_المعلومات(النبات)
خريطة النبات(&:سقي_المعلومات)
نهاية
بالطبع، يمكن إزالة هذه الدالة واستبدالها بمحتواها. من خلال هذا المثال، أردت أن أعرض بشكل عام نمط إعادة الهيكلة.
الملخص
إعادة الهيكلة موضوع كبير. آمل أن يكون هذا المقال حافزاً لقراءة المزيد. هذه مهارات إعادة البناء تساعدك على اكتشاف الأخطاء وتحسين ورشة عمل الكود النظيف. أوصي بقراءة كتاب مارتن (تحسين تصميم الكود الحالي)، وهو مجموعة أساسية ومفيدة جدًا من قواعد إعادة الهيكلة. يعرض المؤلف التحويلات المختلفة خطوة بخطوة مع شرح كامل وتحفيز ونصائح حول كيفية تجنب الأخطاء في إعادة الهيكلة. نظرًا لتعدد استخداماته، فهو كتاب رائع للواجهة الأمامية و مطورو الواجهة الخلفية.