استبدال الاستثناءات بالاختبارات (Replace Exception with Test)

من موسوعة حسوب
< Refactoring
مراجعة 08:36، 26 فبراير 2019 بواسطة جميل-بيلوني (نقاش | مساهمات) (مراجعة وتدقيق.)
(فرق) → مراجعة أقدم | المراجعة الحالية (فرق) | مراجعة أحدث ← (فرق)
اذهب إلى التنقل اذهب إلى البحث

المشكلة

إطلاق استثناء يمكن لاختبار بسيط أن يحل محله.

الحل

يستعاض عن الاستثناء باختبار الحالة.

مثال

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

معالجة الاستثناء الحاصل عند وقوع الفهرس periodNumber خارج حدود مصفوفة القيم values وإعادة القيمة 0 آنذاك:

في لغة Java:

double getValueForPeriod(int periodNumber) {
  try {
    return values[periodNumber];
  } catch (ArrayIndexOutOfBoundsException e) {
    return 0;
  }
}

في لغة C#‎:

double GetValueForPeriod(int periodNumber) 
{
  try 
  {
    return values[periodNumber];
  } 
  catch (IndexOutOfRangeException e) 
  {
    return 0;
  }
}

في لغة PHP:

function getValueForPeriod($periodNumber) {
  try {
    return $this->values[$periodNumber];
  } catch (ArrayIndexOutOfBoundsException $e) {
    return 0;
  }
}

في لغة Python:

def getValueForPeriod(periodNumber):
    try:
        return values[periodNumber]
    except IndexError:
        return 0

في لغة TypeScript:

getValueForPeriod(periodNumber: number): number {
  try {
    return values[periodNumber];
  } catch (ArrayIndexOutOfBoundsException e) {
    return 0;
  }
}

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

استعمال شرط بسيط ينوب مكان التعبير try/catch الذي يعالج الاستثناء المحتمل وقوعه:

في لغة Java:

double getValueForPeriod(int periodNumber) {
  if (periodNumber >= values.length) {
    return 0;
  }
  return values[periodNumber];
}

في لغة C#‎:

double GetValueForPeriod(int periodNumber) 
{
  if (periodNumber >= values.Length) 
  {
    return 0;
  }
  return values[periodNumber];
}

في لغة PHP:

function getValueForPeriod($periodNumber) {
  if ($periodNumber >= count($this->values)) {
    return 0;
  }
  return $this->values[$periodNumber];
}

في لغة Python:

def getValueForPeriod(self, periodNumber):
    if periodNumber >= len(self.values):
        return 0
    return self.values[periodNumber]

في لغة TypeScript:

getValueForPeriod(periodNumber: number): number {
  if (periodNumber >= values.length) {
    return 0;
  }
  return values[periodNumber];
}

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

يجب استخدام الاستثناءات لمعالجة السلوك غير الاعتيادي المتعلق بخطأ غير متوقع. ولا يجب أن تكون بديلًا للاختبارات. وإذا أمكن تجنب الاستثناء بمجرد التحقق من شرطٍ ما قبل التشغيل، فافعل ذلك. ويجب الاحتفاظ بالاستثناءات للأخطاء الحقيقية.

فعلى سبيل المثال، افترض أنَّك دخلت حقل ألغام وأطلقت لغمًا هناك، مما أدى إلى إطلاق استثناء؛ وتم التعامل بنجاح مع الاستثناء وتم رفعك في الهواء إلى بر الأمان خارج حقل الألغام. أتعلم أنه بإمكانك تجنب كل هذا بمجرد قراءة علامة التحذير أمام حقل الألغام؟! بالتأكيد قراءة اللافتة هي الأفضل والأسهل.

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

  • في بعض الأحيان، يمكن أن يكون شرط بسيطة أكثر وضوحًا من شيفرة معالجة الاستثناء.

آلية الحل

  1. إنشاء شرطية لحالة حدية ونقلها قبل كتلة try/catch.
  2. نقل الشيفرة من قسم catch داخل هذه الشرطية.
  3. في قسم catch، ضع الشيفرة لإطلاق استثناء غير مسمى عادي وشغِّل كل الاختبارات.
  4. وإذا لم تُطلق استثناءات خلال الاختبارات، تخلص من التعبير try/catch.

انظر أيضًا

مصادر