تبديل المتغير المؤقت إلى استدعاء(Replace Temp with Query)

من موسوعة حسوب
اذهب إلى: تصفح، ابحث

المشكلة

تخزين نتيجة تعبيرٍ ما (expression) في متغيِّر محليٍّ (local variable) لاستخدامه لاحقًا في الشيفرة.

الحل

نقل التعبير بأكمله إلى تابعٍ (method) مستقلٍ يعيد نتيجته، وعندها سيكون استدعاء هذا التابع بديلًا عن استخدام المتغيِّر (variable)، ومن الممكن أيضًا دمج هذا التابع مع توابع أخرى عند الحاجة للقيام بذلك.

مثال

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

نلاحظ في الشيفرة الآتية وجود متغيِّرٍ مؤقتٍ باسم basePrice لتخزين القيمة الناتجة عن تنفيذ التعبير الرياضيّ بمعامل الجداء (أي المعامل *)، وسيُستخدَم هذا المتغيِّر لاحقًا في الأجزاء الشرطيّة من الشيفرة:

في لغة Java:
double calculateTotal() {
  double basePrice = quantity * itemPrice;
  if (basePrice > 1000) {
    return basePrice * 0.95;
  }
  else {
    return basePrice * 0.98;
  }
}
في لغة #C:
double CalculateTotal() 
{
  double basePrice = quantity * itemPrice;
  
  if (basePrice > 1000) 
  {
    return basePrice * 0.95;
  }
  else 
  {
    return basePrice * 0.98;
  }
}
في لغة PHP:
$basePrice = $this->quantity * $this->itemPrice;
if ($basePrice > 1000) {
  return $basePrice * 0.95;
} else {
  return $basePrice * 0.98;
}
في لغة Python:
def calculateTotal():
    basePrice = quantity * itemPrice
    if basePrice > 1000:
        return basePrice * 0.95
    else:
        return basePrice * 0.98
في لغة TypeScript:
 calculateTotal(): number {
  let basePrice = quantity * itemPrice;
  if (basePrice > 1000) {
    return basePrice * 0.95;
  }
  else {
    return basePrice * 0.98;
  }
}

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

تتمثَّل إعادة التصميم بالاستغناء عن المتغيِّر المؤقّت basePrice في الشيفرة السابقة واستدعاء التابع basePrice()‎ بدلًا عنه، والذي سيعيد نتيجة التعبير الرياضيّ السابق لتصبح الشيفرة كما يلي:

في لغة Java:
double calculateTotal() {
  if (basePrice() > 1000) {
    return basePrice() * 0.95;
  }
  else {
    return basePrice() * 0.98;
  }
}
double basePrice() {
  return quantity * itemPrice;
}
في لغة #C:
double CalculateTotal() 
{
  if (BasePrice() > 1000) 
  {
    return BasePrice() * 0.95;
  }
  else 
  {
    return BasePrice() * 0.98;
  }
}
double BasePrice() 
{
  return quantity * itemPrice;
}
في لغة PHP:
if ($this->basePrice() > 1000) {
  return $this->basePrice() * 0.95;
} else {
  return $this->basePrice() * 0.98;
}

...

function basePrice() {
  return $this->quantity * $this->itemPrice;
}
في لغة Python:
def calculateTotal():
    if basePrice() > 1000:
        return basePrice() * 0.95
    else:
        return basePrice() * 0.98

def basePrice():
    return quantity * itemPrice
في لغة TypeScript:
calculateTotal(): number {
  if (basePrice() > 1000) {
    return basePrice() * 0.95;
  }
  else {
    return basePrice() * 0.98;
  }
}
basePrice(): number {
  return quantity * itemPrice;
}

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

لتمهيد الطريق لتطبيق تقنيّة الحلّ باستخراج التابع (extract method) على جزءٍ من تابعٍ طويلٍ نسبيًا، وإذا ما وُجِد التعبير ذاته في عدّة توابع أخرى في البرنامج فمن الأفضل إنشاء تابعٍ مشترك (common method).

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

  • تحسين قابلية قراءة الشيفرة (readability)، إذ إنّ فهمَ هدف التابع المُسمّى getTax()‎ أسهل بكثيرٍ من محاولة فهم السطر التعبيريّ orderPrice() * 0.2.
  • الحدُّ من تكرار الشيفرة (duplication) وخاصّة عندما يكون التعبير المُستبدَل مُستخدَمًا في عدّة توابع (methods).

الأداء (Performance)

يتساءل الكثيرون: هل يسبِّب تطبيق هذا الحل مشكلةً بالأداء؟ والإجابة الصريحة: نعم! وذلك لأنّ الشيفرة الناتجة عن الحل ستعتمد على استدعاء تابعٍ جديدٍ بدلًا من اعتمادها على متغيِّرٍ محليٍّ، ولكن مع السرعة الفائقة في المعالجات (CPUs) المستخدَمة اليوم والمترجِمات (compilers) الفعّالة فالمشكلة يسيرة، إذ بالمقارنة مع الشيفرة سهلة القراءة والقدرة على إعادة استخدام التابع مرّاتٍ ومراتٍ في البرنامج (واللتان حصلنا عليهما من تطبيق الحل) فلم يعد هنالك أهمية لتلك المشكلة الطفيفة، أمّا إن كان المتغيِّر مستخدمًا لتخزين نتيجة تعبيرٍ مستهلكٍ للوقت ومكلفٍ حقًا فمن الجيّد العدول عن تطبيق تقنية الحل هذه.

آلية الحل

  1. التأكُّد من أن قيمة التعبير مُسندةٌ إلى المتغيِّر مرّةً واحدةً (وواحدة فقط) في التابع (method)، فإن لم يكن كذلك، فعليك الانتقال إلى تقنيّة تجزيء المتغيِّر المؤقَّت (split temporary variable) لضمان استخدام المتغيِّر لتخزين نتيجة التعبير فقط.
  2. تطبيق استخراج التابع (extract method) لوضع التعبير المطلوب في تابعٍ جديدٍ مستقلٍ، والتأكُّد من أنّ هذا التابع الجديد سيعيد قيمة التعبير دون أن يغيّر شيئًا من حالة الكائن (object state)، فإنْ لم يتحقَّق ذلك فيجب عندئذٍ تطبيق تقنيّة عزل الاستدعاء عن المُحدِّد (separate query from modifier).
  3. تبديل المتغيِّر إلى استدعاءٍ للتابع الجديد المُستحدَث.

انظر أيضًا

مصادر