الفرق بين المراجعتين ل"Refactoring/replace nested conditional with guard clauses"

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
(أنشأ الصفحة ب'<noinclude>{{DISPLAYTITLE: تبديل الشرطيات المتداخلة بعبارات الحماية (Replace Nested Conditional with Guard Clauses}}</noinclude> ==...')
 
ط (مراجعة وتدقيق)
 
سطر 1: سطر 1:
 
<noinclude>{{DISPLAYTITLE: تبديل الشرطيات المتداخلة بعبارات الحماية (Replace Nested Conditional with Guard Clauses}}</noinclude>
 
<noinclude>{{DISPLAYTITLE: تبديل الشرطيات المتداخلة بعبارات الحماية (Replace Nested Conditional with Guard Clauses}}</noinclude>
 
== المشكلة ==
 
== المشكلة ==
وجود مجموعة متداخلة من الشرطيات وصعوبة تحديد التدفق الطبيعي لتنفيذ الشيفرة البرمجية.
+
وجود مجموعة متداخلة من الشروط وصعوبة تحديد التدفق الطبيعي لتنفيذ الشيفرة البرمجية.
  
 
== الحل ==
 
== الحل ==
عزل جميع الاختبارات الخاصة والحالات الطرفية في عبارات منفصلة ووضعها قبل الاختبارات الرئيسية. من الناحية المثالية، يجب أن يكون لديك قائمة "مسطحة" من الشرطيات، واحدة تلو الأخرى.
+
عزل جميع الاختبارات الخاصة والحالات الطرفية في عبارات منفصلة ووضعها قبل الاختبارات الرئيسية. من الناحية المثالية، يجب أن يكون لديك قائمة "مسطحة" من الشروط، واحدةً تلو الأخرى.
  
=== مثال ===
+
== مثال ==
 +
 
 +
=== قبل إعادة التصميم ===
 +
يوجد لدينا الدالة <code>()getPayAmount</code> التي تتحقق من القيمة المُستدعاة معها عبر عدد من الشروط المتشعبة - مع استعمال المتغير <code>result</code> للحصول على النتيجة وإعادتها في النهاية - مما يجعل معرفة تدفق ومسار الدالة أمرًا معقدًا:
  
==== قبل إعادة التصميم ====
 
 
في لغة Java:<syntaxhighlight lang="java">
 
في لغة Java:<syntaxhighlight lang="java">
 
public double getPayAmount() {
 
public double getPayAmount() {
سطر 91: سطر 93:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==== بعد إعادة التصميم ====
+
=== بعد إعادة التصميم ===
 +
حذف المتغير <code>result</code> وجعل الشروط المراد التحقق منها في مستوًى واحد ضمن الدالة <code>()getPayAmount</code>:
 +
 
 
في لغة Java:<syntaxhighlight lang="java">
 
في لغة Java:<syntaxhighlight lang="java">
 
public double getPayAmount() {
 
public double getPayAmount() {
سطر 144: سطر 148:
  
 
== لم إعادة التصميم؟ ==
 
== لم إعادة التصميم؟ ==
من السهل إلى حد ما اكتشاف "الشرطية المُّعقدة". وتشكل المسافات البادئة في كل مستوى من التداخلية سهم يشير إلى اليمين في اتجاه الألم والشقاء:<syntaxhighlight lang="text">
+
من السهل إلى حد ما اكتشاف "الشروط المُّعقدة". وكلما ازدادت المسافات البادئة في كل مستوى من التداخلية التي تتجه إلى اليمين في الشيفرة، ازداد بقدرها الألم والشقاء:<syntaxhighlight lang="java">
 
if () {
 
if () {
 
     if () {
 
     if () {
سطر 165: سطر 169:
 
     }
 
     }
 
}
 
}
</syntaxhighlight>ومن الصعب معرفة ما تفعله كل شرطية وكيف، إذ لا يكون التدفق "العادي" لتنفيذ الشيفرة البرمجية واضحًا على الفور. وتشير مثل هذه الشرطيات إلى التطور المتسارع مع إضافة كل شرط كإجراء مؤقت دون أي عناء في التفكير في تحسين الهيكل العام.
+
</syntaxhighlight>ومن الصعب معرفة ما يفعله كل شرط وكيف يتم التحقق منه، إذ لا يكون التدفق "العادي" لتنفيذ الشيفرة البرمجية واضحًا وضوح الشمس. وتشير مثل هذه الشروط إلى التطور المتسارع مع إضافة كل شرط كإجراء مؤقت دون أي عناء في التفكير في تحسين الهيكل العام.
  
لتبسيط الموقف، تُعزل الحالات الخاصة إلى شروط منفصلة تُنهي التنفيذ مباشرة ثم تُعيد قيمة فارغة إذا كانت عبارات الحماية صحيحة. في الواقع، مهمتك هنا هي جعل الهيكل مسطحُا.
+
لتبسيط الموقف، تُعزَل الحالات الخاصة إلى شروط منفصلة تُنهِي التنفيذ مباشرةً ثم تُعيد قيمةً فارغةً إذا كانت عبارات الحماية صحيحة. في الواقع، مهمتك هنا هي جعل الهيكل مسطحُا.
  
 
== آلية الحل ==
 
== آلية الحل ==
محاولة تخليص الشيفرة البرمجية من الآثار الجانبية - قد يكون [[Refactoring/separate query from modifier|فصل الاستعلام عن المُعدِّل]] مفيدًا لهذا الغرض. سيكون هذا الحل ضروريًا لإجراء إعادة التشكيل المبينة أدناه.
+
حاول تخليص الشيفرة البرمجية من الآثار الجانبية؛ قد يكون [[Refactoring/separate query from modifier|فصل الاستعلام عن المُعدِّل]] مفيدًا لهذا الغرض. سيكون هذا الحل ضروريًا لإجراء إعادة التشكيل المبينة أدناه.
 
# اعزل جميع عبارات الحماية التي تؤدي إلى استدعاء استثناء أو إلى الإعادة الفورية لقيمة من التابع. وضع هذه الشروط في بداية التابع.
 
# اعزل جميع عبارات الحماية التي تؤدي إلى استدعاء استثناء أو إلى الإعادة الفورية لقيمة من التابع. وضع هذه الشروط في بداية التابع.
 
# بعد اكتمال إعادة الترتيب وإكمال كافة الاختبارات بنجاح، راجع ما إذا كان يمكنك استخدام [[Refactoring/consolidate conditional expression|توحيد التعبير الشرطي]] لعبارات الحماية التي تؤدي إلى نفس الاستثناءات أو القيم المُعادة.
 
# بعد اكتمال إعادة الترتيب وإكمال كافة الاختبارات بنجاح، راجع ما إذا كان يمكنك استخدام [[Refactoring/consolidate conditional expression|توحيد التعبير الشرطي]] لعبارات الحماية التي تؤدي إلى نفس الاستثناءات أو القيم المُعادة.

المراجعة الحالية بتاريخ 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. بعد اكتمال إعادة الترتيب وإكمال كافة الاختبارات بنجاح، راجع ما إذا كان يمكنك استخدام توحيد التعبير الشرطي لعبارات الحماية التي تؤدي إلى نفس الاستثناءات أو القيم المُعادة.

انظر أيضًا

مصادر