تبديل الشرطيات المتداخلة بعبارات الحماية (Replace Nested Conditional with Guard Clauses

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

المشكلة

وجود مجموعة متداخلة من الشروط وصعوبة تحديد التدفق الطبيعي لتنفيذ الشيفرة البرمجية.

الحل

عزل جميع الاختبارات الخاصة والحالات الطرفية في عبارات منفصلة ووضعها قبل الاختبارات الرئيسية. من الناحية المثالية، يجب أن يكون لديك قائمة "مسطحة" من الشروط، واحدةً تلو الأخرى.

مثال

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

يوجد لدينا الدالة ()getPayAmount التي تتحقق من القيمة المُستدعاة معها عبر عدد من الشروط المتشعبة - مع استعمال المتغير result للحصول على النتيجة وإعادتها في النهاية - مما يجعل معرفة تدفق ومسار الدالة أمرًا معقدًا:

في لغة Java:

public double getPayAmount() {
  double result;
  if (isDead){
    result = deadAmount();
  }
  else {
    if (isSeparated){
      result = separatedAmount();
    }
    else {
      if (isRetired){
        result = retiredAmount();
      }
      else{
        result = normalPayAmount();
      }
    }
  }
  return result;
}

في لغة C#‎:

public double GetPayAmount()
{
  double result;
  
  if (isDead)
  {
    result = DeadAmount();
  }
  else 
  {
    if (isSeparated)
    {
      result = SeparatedAmount();
    }
    else 
    {
      if (isRetired)
      {
        result = RetiredAmount();
      }
      else
      {
        result = NormalPayAmount();
      }
    }
  }
  
  return result;
}

في لغة PHP:

public function getPayAmount() {
  if ($this->isDead)
    $result = $this->deadAmount();
  else {
    if ($this->isSeparated)
      $result = $this->separatedAmount();
    else {
      if ($this->isRetired)
        $result = $this->retiredAmount();
      else
        $result = $this->normalPayAmount();
    }
  }
  return $result;
}

في لغة Python:

def getPayAmount(self):
    if self.isDead:
        result = deadAmount()
    else:
        if self.isSeparated:
            result = separatedAmount()
        else:
            if self.isRetired:
                result = retiredAmount()
            else:
                result = normalPayAmount()
    return result

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

حذف المتغير result وجعل الشروط المراد التحقق منها في مستوًى واحد ضمن الدالة ()getPayAmount:

في لغة Java:

public double getPayAmount() {
  if (isDead){
    return deadAmount();
  }
  if (isSeparated){
    return separatedAmount();
  }
  if (isRetired){
    return retiredAmount();
  }
  return normalPayAmount();
}

في لغة C#‎:

public double GetPayAmount() 
{
  if (isDead)
  {
    return DeadAmount();
  }
  if (isSeparated)
  {
    return SeparatedAmount();
  }
  if (isRetired)
  {
    return RetiredAmount();
  }
  return NormalPayAmount();
}

في لغة PHP:

public function getPayAmount() {
  if ($this->isDead)
    return $this->deadAmount();
  if ($this->isSeparated)
    return $this->separatedAmount();
  if ($this->isRetired)
    return $this->retiredAmount();
  return $this->normalPayAmount();
}

في لغة Python:

def getPayAmount(self):
    if self.isDead:
        return deadAmount()
    if self.isSeparated:
        return separatedAmount()
    if self.isRetired:
        return retiredAmount()
    return normalPayAmount()

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

من السهل إلى حد ما اكتشاف "الشروط المُّعقدة". وكلما ازدادت المسافات البادئة في كل مستوى من التداخلية التي تتجه إلى اليمين في الشيفرة، ازداد بقدرها الألم والشقاء:

if () {
    if () {
        do {
            if () {
                if () {
                    if () {
                        ...
                    }
                }
                ...
            }
            ...
        }
        while ();
        ...
    }
    else {
        ...
    }
}

ومن الصعب معرفة ما يفعله كل شرط وكيف يتم التحقق منه، إذ لا يكون التدفق "العادي" لتنفيذ الشيفرة البرمجية واضحًا وضوح الشمس. وتشير مثل هذه الشروط إلى التطور المتسارع مع إضافة كل شرط كإجراء مؤقت دون أي عناء في التفكير في تحسين الهيكل العام.

لتبسيط الموقف، تُعزَل الحالات الخاصة إلى شروط منفصلة تُنهِي التنفيذ مباشرةً ثم تُعيد قيمةً فارغةً إذا كانت عبارات الحماية صحيحة. في الواقع، مهمتك هنا هي جعل الهيكل مسطحُا.

آلية الحل

حاول تخليص الشيفرة البرمجية من الآثار الجانبية؛ قد يكون فصل الاستعلام عن المُعدِّل مفيدًا لهذا الغرض. سيكون هذا الحل ضروريًا لإجراء إعادة التشكيل المبينة أدناه.

  1. اعزل جميع عبارات الحماية التي تؤدي إلى استدعاء استثناء أو إلى الإعادة الفورية لقيمة من التابع. وضع هذه الشروط في بداية التابع.
  2. بعد اكتمال إعادة الترتيب وإكمال كافة الاختبارات بنجاح، راجع ما إذا كان يمكنك استخدام توحيد التعبير الشرطي لعبارات الحماية التي تؤدي إلى نفس الاستثناءات أو القيم المُعادة.

انظر أيضًا

مصادر