تبديل التابع إلى كائن التابع (Replace Method with Method Object)

من موسوعة حسوب
مراجعة 06:37، 12 نوفمبر 2018 بواسطة جميل-بيلوني (نقاش | مساهمات) (إنشاء الصفحة. هذه الصفحة من مساهمات "نور تامر".)
(فرق) → مراجعة أقدم | المراجعة الحالية (فرق) | مراجعة أحدث ← (فرق)

المشكلة

وجود تابعٍ طويلٍ بالكثير من المتغيِّرات المحليّة (local variables) المتداخلة والتي تحول دون تطبيق تقنية الحل باستخراج التابع (extract method).

الحل

نقل التابع إلى صنفٍ (class) مستقلٍ بحيث تصبح متغيِّراته المحليّة حقولًا (fields) لهذا الصنف، وتقسيم التابع بعد ذلك إلى عدّة توابع أصغر في الصنف ذاته.

مثال

قبل إعادة التصميم

نلاحظ وجود العديد من المتغيِّرات المحليّة في التابع price()‎ بالإضافة إلى عملياتٍ أخرى قد تكون طويلةً ومعقَّدة:

class Order {
  //...
  public double price() {
    double primaryBasePrice;
    double secondaryBasePrice;
    double tertiaryBasePrice;
    // عمليات أخرى طويلة
    //...
  }
}

بعد إعادة التصميم

يُعرَّف الصنف (class) الجديد باسم PriceCalculator والذي يحتوي على حقولٍ خاصَّة (private fields) متوافقةٍ مع المتغيِّرات المحليِّة للتابع الأصليّ، كما ويحتوي بانيًا (constructor) يُستخدم للتهيئة الأوليّة (intialization) لتلك الحقول وتابعٌ رئيسيّ (وهو التابع compute()‎) يحتوي على العمليات الواردة في التابع الأصليّ (وقد تُجزَّئ لعدَّة توابع)، ثمَّ يُستبدَل محتوى التابع الأصليّ (وهو التابع price()‎) ليصبح استدعاءً للتابع الأساسيّ عبر كائنٍ (instance) من الصنف الجديد، لتصبح الشيفرة بعد إعادة التصميم بالشكل الآتي:

class Order {
  //...
  public double price() {
    return new PriceCalculator(this).compute();
  }
}

class PriceCalculator {
  private double primaryBasePrice;
  private double secondaryBasePrice;
  private double tertiaryBasePrice;
  
  public PriceCalculator(Order order) {
    // نسخ المعلومات المرتبطة من الكائن
    //...
  }
  
  public double compute() {
    // عمليات أخرى طويلة
    //...
  }
}

لم إعادة التصميم؟

لأنّ التوابع الطويلة غالبًا ما تحتوي على الكثير من المتغيِّرات المحليّة المترابطة ويصعُب فصلها عن بعضها، فتكون الخطوة الأولى بعزل التابع بأكمله في صنفٍ (class) جديدٍ مستقلٍّ تصبح متغيِّرات التابع حقولًا له، ويبدو هذا الحل جيدًا لسببين:

  1. سيصبح حل المشكلة على مستوى الأصناف (classes) بدلًا من التوابع (methods).
  2. سيمهِّد الطريق لتقسيم التابع الضخم إلى توابع أصغر في الصنف الجديد، ولا علاقة لها بالصنف الأصليّ الذي استُخرِج منه التابع.

فوائد تطبيق الحل

الحدُّ من تضخِّم التابع وسهولة تقسيمه إلى توابع فرعيّة (submethods) أصغر في الصنف ذاته دون العبث بالصنف الأصليَّ الذي قد يحتوي على توابع أخرى.

مساوئ تطبيق الحل

إضافة صنفٍ جديدٍ ممّا يؤدي لزيادة تعقيد (complexity) البرنامج عمومًا.

آلية الحل

  1. إنشاء صنفٍ (class) جديدٍ، وتسميته بما يناسب هدف التابع المُعاد تصميمه.
  2. إنشاء حقل خاصّ (private field) بالصنف الجديد، وذلك لتخزين مرجعيّةٍ (reference) إلى كائن (instance) الصنف الأصليّ للتابع، والهدف من ذلك هو الحصول على بعض البيانات المطلوبة من الصنف الأصليّ عند الحاجة إليها.
  3. إنشاء حقلٍ خاصٍ لكلّ متغيِّرٍ محليٍّ (local variables) من التابع.
  4. إنشاء بانٍ (constructor) يقبل كافة قيم المتغيِّرات المحليّة كمعاملات (parameters) له ليقوم بالتهيئة الأوليّة للحقول الخاصَّة بالاعتماد عليها.
  5. التصريح عن التابع الرئيسيّ (main method) ونسخ الشيفرة الأصليّة للتابع إليه مع تبديل كلِّ متغيِّرٍ إلى الحقل (field) الموافق له.
  6. تعديل محتوى التابع الأصليّ (في صنفه السابق) عبر إنشاء كائن التابع (method object) واستدعاء التابع الأساسيِّ فيه.

انظر أيضًا

مصادر