تقنيات إعادة التصميم (Refactoring Techniques)

من موسوعة حسوب

إنشاء التوابع

تستهدف إعادة التصميم بشكل رئيسيٍّ إنشاء التوابع الصحيحة المناسبة، إذ تكون التوابع الطويلة سببًا للمشاكل في كثيرٍ من الحالات، وتجعل شيفرات بعض التوابع منطق التنفيذ (execution logic) غامضًا ويصبح التابع بهذا عصيَّ الفهم من جهةٍ وصعب التغييرٍ من جهة ثانية.

يشمل هذا القسم من الحلول كلَّ ما يتعلق بالتوابع وإزالة التكرار (duplicates) في الشيفرة ليسمح بإجراء التطويرات المستقبليّة، وهذه التقنيات هي:

  1. استخراج التوابع (Extract Methods): والتي تتمثل بوجود أجزاء من الشيفرة يُمكن عزلها وتجميعها سويةً.
  2. دمج التوابع (Inline Methods): مثل أن يكون محتوى التابع (method body) بسيطًا وواضحًا أكثر من التابع بحدِّ ذاته، ويمكن عندئذٍ الاستغناء عنه.
  3. استخراج المتغيِّرات (Extract Variables): مثل وجود تعبيرٍ (expression) معقِّد يصعُب فهمه.
  4. دمج المتغيِّر المؤقّت (Inline Temp): والذي يتمثَّل بوجود متغيِّرٍ مؤقَّت (temporary) لحفظ قيمة تعبيرٍ (expression) بسيطٍ ولا شيء آخر سواه.
  5. تبديل المتغير الوسيط إلى استدعاء (Replace Temp with Query): عبر تخزين نتيجة تعبيرٍ ما (expression) في متغيِّر محليٍّ (local variable) لاستخدامه لاحقًا في الشيفرة.
  6. تجزئة المتغيِّر المؤقَّت (Split Temporary Variable): مثل وجود متغيِّرٍ محليّ يُستخدَم لتخزين عدّة قيمٍ مؤقتةٍ (مرحليّة) داخل التابع.
  7. إزالة الإسناد إلى المعاملات (Remove Assignments to Parameters): عبر إسناد قيمةٍ ما إلى أحد المعاملات (parameter) داخل التابع (method body).
  8. تبديل التوابع إلى كائنات التوابع (Replace Method with Method Object): والذي يتمثَّل بوجود تابعٍ طويلٍ بالكثير من المتغيِّرات المحليّة (local variables) المتداخلة والتي تحول دون تطبيق تقنية الحل باستخراج التابع (extract method).
  9. استبدال الخوارزمية (Substitute Algorithm): عندما يكون هنالك حاجة إلى استبدال خوارزميّة ما بخوارزميّة أخرى.

نقل الميزات ما بين الكائنات

تساعد عملية إعادة التصميم في توزيع المهام بشكل مثاليّ على الأصناف المختلفة في الشيفرة، وتضمن تقنيات الحل هذه طريقةً آمنةً لنقل المهام ما بين الأصناف، وإنشاء أصناف جديدة وحماية تفاصيل عملية التنفيذ من الوصول العام، وهذه التقنيات تشمل:

  1. نقل التابع (Move Method): إذ يستخدم تابع في صنفٍ ما أكثر من استخدامه في صنفه الأساسيّ.
  2. نقل الحقول (Move Fields): إذ يستخدم حقل في صنفٍ ما أكثر من استخدامه في صنفه الأساسيّ.
  3. استخراج الصنف (Extract Class): مثل وجود صنفٍ واحدٍ يقوم بمهامٍ عديدةٍ يمكن توزيعها على صنفين.
  4. دمج الصنف (Inline Class): مثل وجود صنفٍ لا يقوم بأيّ مهمّة فعليّة ولا يُخطَّط لإضافة مهامٍ إليه لاحقًا.
  5. إخفاء التفويض (Hide Delegate): كأن يصل العميل إلى كائنٍ ما وليكن الكائن B من أحد حقول أو توابع كائنٍ آخر وليكن A، ومن ثمّ يستدعي تابعًا لهذا الكائن B.
  6. الاستغناء عن الوسيط (Remove Middle Man): والذي يتمثَّل باحتواء الصنف على العديد من التوابع التي تنقل سياق البرنامج إلى كائنات أخرى.
  7. تعريف التوابع الدخيلة (Introduce Foreign Methods): مثل الحاجة إلى تابعٍ غير موجودٍ في الصنف المساعد ومن غير الممكن إضافته إلى ذلك الصنف.
  8. تعريف الإضافات المحلية (Introduce Local Extensions): مثل الحاجة إلى بعض التوابع (methods) غير الموجودة في الصنف المساعد (utility class)، ومن غير الممكن إضافتها إلى ذلك الصنف.

تنظيم البيانات

تساعد تقنيات إعادة التصميم هذه بالتعامل مع البيانات، وتبديل أصناف ذات وظائف كثيرة مكان الأنواع الأساسية (primitives). نتيجة أخرى مهمة نحصل عليها بتطبيق هذه التقنيات هي فك ارتباطات صنف مما يجعل الصنف قابلًا للنقل وإعادة الاستعمال. وهذه التقنيات هي:

  1. التغليف الداخلي للحقول (Self Encapsulate Fields): وهو الوصول المباشر إلى الحقول الخاصّة داخل الصنف.
  2. تبديل قيم البيانات إلى كائنات (Replace Data Values with Objects): مثل وجود حقلٍ مٌخصَّص للبيانات في صنفٍ ما (أو في عددٍ من الأصناف)، ولهذا الحقل بياناته وسلوكه المرتبط به.
  3. تبديل القيمة إلى مرجع (Change Value to Reference): مثل وجود العديد من النُسَخ المتماثلة من صنفٍ واحدٍ تحتاج إلى استبدال كائنٍ واحدٍ بها.
  4. تبديل المرجع إلى قيمة (Change Reference to Value): مثل وجود كائن مرجع صغير جدًا نادرًا ما يتغيَّر لتبرير إدارة دورة حياته.
  5. تبديل المصفوفات بكائنات (Replace Array with Object): إذ يكون لديك مصفوفة تحتوي على أنواع مختلفة من البيانات.
  6. تكرار البيانات المرُاقَبة (Duplicate Observed Data): هل بيانات النطاق المخزَّنة في أصناف هي المسؤولة عن واجهة المستخدم الرسومية (GUI)؟ إذًا، إليك الحل.
  7. تغيير الاقتران أحادي الاتجاه إلى ثنائي الاتجاه (Change Unidirectional Association to Bidirectional): وهو وجود صنفان يحتاج كل منهما إلى استخدام ميزات الآخر، ولكن الاقتران بينهما أحادي الاتجاه فقط.
  8. تغيير الاقتران ثنائي الاتجاه إلى أحادي الاتجاه (Change Bidirectional Association to Unidirectional): وهو وجود اقتران ثنائي الاتجاه (bidirectional association) بين الأصناف، ولكن لا يستخدم أحد الأصناف الميزات الأخرى.
  9. تبديل الأعداد السحرية بثوابت رمزية (Replace Magic Number with Symbolic Constant): إذ تستخدم الشيفرة البرمجية عددًا له معنىً معين له.
  10. تغليف الحقول (Encapsulate Field): فينشأ لديك حقل عام.
  11. تغليف المجموعات (Encapsulate Collection): ويتمثَّل باحتواء صنف على حقل مجموعة وجالب (getter) وضابط (setter) بسيط للعمل مع المجموعة.
  12. تبديل رموز الأنواع بالأصناف (Replace Type Code with Class): إذ يحتوي الصنف على حقل يحتوي على رموز الأنواع. ولا تُستخدم قيم هذا النوع في شروط المُشغِّل ولا تؤثر على سلوك البرنامج.
  13. تبديل رموز الأنواع بالأصناف الفرعية (Replace Type Code with Subclasses): إذ يؤثر النوع المُرمَّز على سلوك البرنامج (تُطلِق قيم هذا الحقل رموز مختلفة في الشرطيات).
  14. تبديل رموز الأنواع بالحالة/الاستراتيجية (Replace Type Code with State/Strategy): ويتمثَّل بتأثير نوع مُرمَّز على سلوك البرنامج ولكن لا يمكن استخدام الأصناف الفرعية للتخلص منه.
  15. استبدال الأصناف الفرعية بالحقول (Replace Subclass with Fields): لديك أصناف فرعية تختلف فقط في توابع (إعادة الثوابت) الخاصة بها.

تبسيط التعابير الشرطية

تزداد البنية المنطقية للشروط تعقيدًا مع مرور الوقت، لذا هنالك الكثير من التقنيات لمواجهة هذا التعقيد وتبسيطه وهي:

  1. تجزئة الشَرطيات (Decompose Conditional): وهو وجود شَرط مُعقد (if-then/else أو switch).
  2. توحيد التعبير الشرطي (Consolidate Conditional Expression): ويتمثَّل بوجود عدة شروط تؤدي إلى نفس النتيجة أو الإجراء.
  3. توحيد الأجزاء الشرطية المكررة (Consolidate Duplicate Conditional Fragments): وهو وجود شيفرة برمجية متطابقة في جميع فروع الشَرطيات.
  1. إزالة رايات التحكم (Remove Control Flag): لديك متغيرات منطقية تعمل كرايات تحكم لتعبيرات منطقية متعددة.
  2. تبديل الشرطيات المتداخلة بعبارات الحماية (Replace Nested Conditional with Guard Clauses: وهو وجود مجموعة متداخلة من الشروط وصعوبة تحديد التدفق الطبيعي لتنفيذ الشيفرة البرمجية.
  3. تبديل الشرطيات بالتعدديّة الشكليّة (Replace Conditional with Polymorphism): مثل وجود شروط تنفِّذ إجراءات مختلفة اعتمادًا على نوع الكائن أو خصائصه.
  4. تقديم الكائن الفارغ (Introduce Null Object): إذ تؤدي إعادة بعض التوابع للقيمة null بدلًا من الكائنات الحقيقية إلى امتلاء الشيفرة البرمجية بالعديد من نقاط التحقق من القيمة null.
  5. تقديم التوكيد (Introduce Assertion): فلكي يعمل جزء من الشيفرة البرمجية بشكل صحيح، يجب أن تتحقق بعض الشروط أو تكون القيم صحيحة.

تبسيط استدعاءات التوابع

تجعل التقنيات التي سيشار إليها في هذا القسم استدعاءات التوابع أبسط وأسهل للفهم والاستيعاب. سيؤدي ذلك بدوره إلى تبسيط الواجهات للتفاعل بين الأصناف. هذه التقنيات هي:

  1. إعادة تسمية التوابع (Rename Method): إذ لا يعبِّر اسم التابع عن ما يقوم به.
  2. إضافة المعاملات (Add Parameter): لأن التابع لا يملك بيانات كافية لتنفيذ بعض الإجراءات.
  3. حذف المعاملات (Remove Parameter): لعدم استخدامها في متن التابع.
  4. فصل الاستعلامات عن المُعدِّلات (Separate Query from Modifier): لأنه لديك تابعٌ يُعيد قيمةً ما ولكن يغيِّر أيضا شيئًا ما داخل الكائن.
  5. تحويل التوابع إلى معاملات (Parameterize Method): تؤدي توابع متعددة أعمالًا مماثلة تختلف فقط من حيث قيمها الداخلية أو أرقامها أو عملياتها.
  6. استبدال المعامل بتوابع صريحة (Replace Parameter with Explicit Methods): إذ ينقسم التابع إلى أجزاء، كل منها يتم تشغيله اعتمادًا على قيمة المعامل.
  7. الحفاظ على الكائن كاملًا (Preserve Whole Object): وهو جلب عدة قيم من أحد الكائنات، ثم تمريرها كمعاملات إلى أحد التوابع.
  8. تبديل المعاملات باستدعاءات التوابع (Replace Parameter with Method Call): وهو استدعاء تابع استعلام وتمرير نتائجه كمعاملات لتابع آخر، في حين أنه يمكن لهذا التابع استدعاء الاستعلام مباشرة.
  9. تعريف كائن المُعامل (Introduce Parameter Object): إذ تحتوي التوابع على نفس المجموعة المتكررة من المعاملات.
  10. ازالة توابع الإعدادات (Remove Setting Method): فيكون تعيين قيمة الحقل فقط عند إنشائه، ولا تتغير في أي وقت لاحق.
  11. إخفاء التابع (Hide Method): فلا يُستخدم التابع من قِبل الأصناف الأخرى أو يستخدم فقط داخل التسلسل الهرمي للصنف الخاص به.
  12. استبدال المُنشئ بتابع التصميم (Replace Constructor with Factory Method): لديك مُنشئ (constructor) معقد يقوم بما هو أكثر من مجرد وضع قيم المعامل في حقول الكائن.
  13. استبدال شيفرات الأخطاء باستثناءات (Replace Error Code with Exception): إذ يعيد التابع قيمة خاصة تشير إلى خطأ.
  14. استبدال الاستثناءات بالاختبارات (Replace Exception with Test): إطلاق استثناء يمكن لاختبار بسيط أن يحل محله.

التعامل مع التعميم

يملك التجريد (Abstraction) تقنيات إعادة التصميم الخاصة به والمرتبطة بشكل أساسي بوظيفة النقل على طول التسلسل الهرمي لوراثة الصنف (class inheritance hierarchy)، وبإنشاء أصناف وواجهات جديدة، وبتبديل التفويض مكان الوراثة أو العكس. تقنيات هذا القسم هي:

  1. استخراج الأصناف الفرعية (Extract Subclass): إذ يكون للصنف ميزات تستعمل فقط في حالات معينة.
  2. استخراج الأصناف الفائقة (Extract Superclass): والذي يتمثَّل بوجود صنفين لهما حقول وتوابع مشتركة.
  3. استخرج الواجهات (Extract Interface): إذ يستخدم العديد من العملاء نفس الجزء من واجهة الصنف. حالة أخرى: عندما يوجد نفس الجزء من الواجهة في صنفين.
  4. هدم التسلسل الهرمي (Collapse Hierarchy): ففي التسلسل الهرمي لصنف، يكون صنفٌ فرعي هو عمليًا نفس صنفه الأب.
  5. تكوين قالب تابع (Form Template Method): تُنفِّذ الأصناف الفرعية خوارزميات تحتوي على خطوات مماثلة في نفس الترتيب.
  6. استبدال التوريث بالتفويض (Replace Inheritance with Delegation): ويتمثَّل باستخدام صنف فرعي جزءًا فقط من توابع صنفه الأب (أو من غير الممكن وراثة بيانات الصنف الأب).
  7. استبدال التفويض بالتوريث (Replace Delegation with Inheritance): إذ يحتوي الصنف على العديد من التوابع البسيطة التي تفوِّض إلى كل التوابع في صنفٍ آخر.
  8. سحب الحقل لأعلى (Pull Up Field): مثل أن يحتوي صنفان على نفس الحقل.
  9. سحب التابع لأعلى (Pull Up Method): وهو أن تحتوي الأصناف الفرعية على توابع تؤدي نفس العمل.
  10. سحب متن المُنشِئ لأعلى (Pull Up Constructor Body): مثل احتواء الأصناف الفرعية على مُنشِئات لها شيفرة متطابقة في أغلبها.
  11. دفع التابع لأسفل (Push Down Method): هل السلوك المُنفَّذ في الصنف الأب مُستخدمٌ في صنف فرعي واحد فقط (أو أكثر)؟ إذًا إليك الحل.
  12. دفع الحقل لأسفل (Push Down Field): هل يستخدم الحقل في بعض الأصناف الفرعية فقط؟ إذًا إليك الحل.

مصادر