سؤال

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

هل هناك أي طرق أخرى وجدتها قد تكون مفيدة لي ولأي شخص آخر يواجه نفس المشكلة؟

مثال، كل ذلك في سطر واحد:

if (var1 = true && var2 = true && var2 = true && var3 = true && var4 = true && var5 = true && var6 = true)
{

مثال متعدد الأسطر:

if (var1 = true && var2 = true && var2 = true
 && var3 = true && var4 = true && var5 = true
 && var6 = true)
{

مثال متداخل:

if (var1 = true && var2 = true && var2 = true && var3 = true)
{
     if (var4 = true && var5 = true && var6 = true)
     {
هل كانت مفيدة؟

المحلول

قم بفصل الشرط إلى عدة منطقيات، ثم استخدم قيمة منطقية رئيسية كشرط.

bool isOpaque = object.Alpha == 1.0f;
bool isDrawable = object.CanDraw && object.Layer == currentLayer;
bool isHidden = hideList.Find(object);

bool isVisible = isOpaque && isDrawable && ! isHidden;

if(isVisible)
{
    // ...
}

افضل حتى الآن:

public bool IsVisible {
    get
    {
        bool isOpaque = object.Alpha == 1.0f;
        bool isDrawable = object.CanDraw && object.Layer == currentLayer;
        bool isHidden = hideList.Find(object);

        return isOpaque && isDrawable && ! isHidden;
    }
}

void Draw()
{
     if(IsVisible)
     {
         // ...
     }
}

تأكد من إعطاء اسم المتغيرات الخاصة بك الذي يشير فعليًا إلى النية وليس الوظيفة.سيساعد هذا المطور بشكل كبير في الحفاظ على الكود الخاص بك ...من الممكن أن تكون انت!

نصائح أخرى

أنا مندهش أنه لم يحصل أحد على هذا بعد.هناك إعادة هيكلة خصيصًا لهذا النوع من المشكلات:

http://www.refactoring.com/catalog/decomposeConditional.html

هناك مسألتان يجب معالجتهما هنا:سهولة القراءة والفهم

يعد حل "سهولة القراءة" مشكلة تتعلق بالأسلوب وبالتالي فهو مفتوح للتفسير.تفضيلي هو هذا:

if (var1 == true && // Explanation of the check
    var2 == true && // Explanation of the check
    var3 == true && // Explanation of the check
    var4 == true && // Explanation of the check
    var5 == true && // Explanation of the check
    var6 == true)   // Explanation of the check
    { }

أو هذا:

if (var1 && // Explanation of the check
    var2 && // Explanation of the check
    var3 && // Explanation of the check
    var4 && // Explanation of the check
    var5 && // Explanation of the check
    var6)   // Explanation of the check
    { }

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

/// <Summary>
/// Tests whether all the conditions are appropriately met
/// </Summary>
private bool AreAllConditionsMet (
    bool var1,
    bool var2,
    bool var3,
    bool var4,
    bool var5,
    bool var6)
{
    return (
        var1 && // Explanation of the check
        var2 && // Explanation of the check
        var3 && // Explanation of the check
        var4 && // Explanation of the check
        var5 && // Explanation of the check
        var6);  // Explanation of the check
}

private void SomeMethod()
{
    // Do some stuff (including declare the required variables)
    if (AreAllConditionsMet (var1, var2, var3, var4, var5, var6))
    {
        // Do something
    }
}

الآن عند المسح البصري لطريقة "SomeMethod"، يتم إخفاء التعقيد الفعلي لمنطق الاختبار ولكن يتم الاحتفاظ بالمعنى الدلالي ليتمكن البشر من فهمه على مستوى عالٍ.إذا كان المطور يحتاج حقًا إلى فهم التفاصيل، فيمكن فحص طريقة AreAllConditionsMet.

يُعرف هذا رسميًا باسم نمط إعادة البناء "التحلل الشرطي" على ما أعتقد.أدوات مثل Resharper أو Refactor Pro!يمكن أن يجعل القيام بهذا النوع من إعادة البناء أمرًا سهلاً!

في جميع الحالات، المفتاح للحصول على تعليمات برمجية قابلة للقراءة والفهم هو استخدام أسماء متغيرات واقعية.بينما أفهم أن هذا مثال مفتعل، فإن "var1" و"var2" وما إلى ذلك لا أسماء المتغيرات المقبولةيجب أن يكون لديهم اسم يعكس الطبيعة الأساسية للبيانات التي يمثلونها.

سأقسمها غالبًا إلى متغيرات منطقية مكونة:

bool orderValid = orderDate < DateTime.Now && orderStatus != Status.Canceled;
bool custValid = customerBalance == 0 && customerName != "Mike";
if (orderValid && custValid)
{
...

أولاً، سأقوم بإزالة جميع == true أجزاء، من شأنها أن تجعلها أقصر بنسبة 50٪ ;)

عندما يكون لدي حالة كبيرة أبحث عن الأسباب.في بعض الأحيان أرى أنه يجب علي استخدام تعدد الأشكال، وأحيانًا أحتاج إلى إضافة بعض كائنات الحالة.في الأساس، هذا يعني أن هناك حاجة إلى إعادة البناء (رائحة الكود).

في بعض الأحيان أستخدم قوانين دي مورغان لتبسيط التعبيرات المنطقية قليلاً.

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

على سبيل المثال، إذا كان لديك طريقة تفعل شيئًا ما، ولكن هناك شروطًا معينة حيث لا ينبغي لها أن تفعل شيئًا ما، بدلاً من:

public void doSomething() {
    if (condition1 && condition2 && condition3 && condition4) {
        // do something
    }
}

يمكنك تغييره إلى:

public void doSomething() {
    if (!condition1) {
        return;
    }

    if (!condition2) {
        return;
    }

    if (!condition3) {
        return;
    }

    if (!condition4) {
        return;
    }

    // do something
}

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

أوصي بشدة بهذا الكتاب بالمناسبة.

لقد رأيت الكثير من الأشخاص والمحررين إما يضعون مسافة بادئة لكل شرط في عبارة if الخاصة بك بعلامة تبويب واحدة، أو يطابقونها بالقوس المفتوح:

if (var1 == true
    && var2 == true
    && var3 == true
   ) {
    /* do something.. */
}

عادةً ما أضع قوس الإغلاق على نفس سطر الشرط الأخير:

if (var1 == true
    && var2 == true
    && var3 == true) {
    /* do something.. */
}

لكنني لا أعتقد أن هذا نظيف تمامًا.

نصيحة ستيف ماكونيل، من اكتمال الكود:استخدام جدول متعدد الأبعاد.يعمل كل متغير بمثابة فهرس للجدول ، ويتحول العبارة IF إلى البحث عن الجدول.على سبيل المثال ، إذا كان (size == 3 && وزن> 70) يترجم إلى قرار إدخال الجدول [الحجم] [Weight_Group

حاول النظر إلى الدالات والمسندات.يحتوي مشروع Apache Commons على مجموعة رائعة من الكائنات للسماح لك بتغليف المنطق الشرطي في الكائنات.مثال على استخدامها متاح على O'reilly هنا.مقتطف من مثال الكود:

import org.apache.commons.collections.ClosureUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.functors.NOPClosure;

Map predicateMap = new HashMap();

predicateMap.put( isHonorRoll, addToHonorRoll );
predicateMap.put( isProblem, flagForAttention );
predicateMap.put( null, ClosureUtils.nopClosure() );

Closure processStudents = 
    ClosureUtils.switchClosure( predicateMap );

CollectionUtils.forAllDo( allStudents, processStudents );

الآن تفاصيل كل مسندات isHonorRoll وعمليات الإغلاق المستخدمة لتقييمها:

import org.apache.commons.collections.Closure;
import org.apache.commons.collections.Predicate;

// Anonymous Predicate that decides if a student 
// has made the honor roll.
Predicate isHonorRoll = new Predicate() {
  public boolean evaluate(Object object) {
    Student s = (Student) object;

    return( ( s.getGrade().equals( "A" ) ) ||
            ( s.getGrade().equals( "B" ) && 
              s.getAttendance() == PERFECT ) );
  }
};

// Anonymous Predicate that decides if a student
// has a problem.
Predicate isProblem = new Predicate() {
  public boolean evaluate(Object object) {
    Student s = (Student) object;

    return ( ( s.getGrade().equals( "D" ) || 
               s.getGrade().equals( "F" ) ) ||
             s.getStatus() == SUSPENDED );
  }
};

// Anonymous Closure that adds a student to the 
// honor roll
Closure addToHonorRoll = new Closure() {
  public void execute(Object object) {
    Student s = (Student) object;

    // Add an award to student record
    s.addAward( "honor roll", 2005 );
    Database.saveStudent( s );
  }
};

// Anonymous Closure flags a student for attention
Closure flagForAttention = new Closure() {
  public void execute(Object object) {
    Student s = (Student) object;

    // Flag student for special attention
    s.addNote( "talk to student", 2005 );
    s.addNote( "meeting with parents", 2005 );
    Database.saveStudent( s );
  }
};

حسنًا، أولاً، لماذا لا:

إذا (var1 && var2 && var2 && var3 && var4 && var5 && var6) {
...

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

إنه ليس أفضل، لكن ما فعلته في الماضي:(تمنع الطريقة التالية إجراء اختبار منطقي قصير الدائرة، ويتم تشغيل جميع الاختبارات حتى لو كان الاختبار الأول خاطئًا.ليس نمطًا موصى به إلا إذا كنت تعلم أنك تحتاج دائمًا إلى تنفيذ جميع التعليمات البرمجية قبل العودة - شكرًا لـ ptomato لاكتشاف خطأي!)

موافق منطقي = cond1؛
طيب &= cond2;
طيب &= cond3;
طيب &= cond4;
طيب &= cond5;
طيب &= cond6;

وهو نفس: (ليس هو نفسه، انظر الملاحظة أعلاه!)

ok = (cond1 && cond2 && cond3 && cond4 && cond5 && cond6);

ألجأ إلى قيم منطقية منفصلة:

Bool cond1 == (var1 && var2);
Bool cond2 == (var3 && var4);

if ( cond1 && cond2 ) {}

كما ذكر آخرون، أود أن أقوم بتحليل الشروط الشرطية الخاصة بك لمعرفة ما إذا كانت هناك طريقة يمكنك من خلالها الاستعانة بمصادر خارجية لطرق أخرى لزيادة إمكانية القراءة.

في اللغات العاكسة مثل PHP، يمكنك استخدام المتغيرات المتغيرة:

$vars = array('var1', 'var2', ... etc.);
foreach ($vars as $v)
    if ($$v == true) {
        // do something
        break;
    }

أحب تقسيمها حسب المستوى، لذا سأقوم بتنسيق المثال على النحو التالي:

if (var1 = true
 && var2 = true
 && var2 = true
 && var3 = true
 && var4 = true
 && var5 = true
 && var6 = true){

إنه مفيد عندما يكون لديك المزيد من التداخل، مثل هذا (من الواضح أن الظروف الحقيقية ستكون أكثر إثارة للاهتمام من "= صحيح" لكل شيء):

if ((var1 = true && var2 = true)
 && ((var2 = true && var3 = true)
  && (var4 = true && var5 = true))
 && (var6 = true)){

إذا كنت تبرمج بلغة بايثون، فهذا أمر مناسب مع الميزات المضمنة all() يتم تطبيق الوظيفة على قائمة المتغيرات الخاصة بك (سأستخدم فقط القيم الحرفية المنطقية هنا):

>>> L = [True, True, True, False, True]
>>> all(L) # True, only if all elements of L are True.
False
>>> any(L) # True, if any elements of L are True.
True

هل هناك أي وظيفة مقابلة في لغتك (C#؟جافا؟).إذا كان الأمر كذلك، فمن المحتمل أن يكون هذا هو النهج الأنظف.

ماكدويل،

أنت على صواب أنه عند استخدام عامل التشغيل "&" المفرد الذي يتم تقييمه على جانبي التعبير.ومع ذلك، عند استخدام عامل التشغيل '&&' (على الأقل في C#)، فإن أول تعبير يُرجع خطأ هو آخر تعبير تم تقييمه.وهذا يجعل وضع التقييم قبل عبارة FOR جيدًا مثل أي طريقة أخرى للقيام بذلك.

@ قرص

إنه ليس أفضل، لكن ما فعلته في الماضي:

موافق منطقي = cond1؛طيب &= cond2;طيب &= cond3;طيب &= cond4;طيب &= cond5;طيب &= cond6;

وهو نفس:

ok = (cond1 && cond2 && cond3 && cond4 && cond5 && cond6);

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

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

do {
    if (!cond1)
       break;
    if (!cond2)
       break;
    if (!cond3)
       break;
    ...
    DoSomething();
} while (false);

بينما (كاذب) نوع من الجبن.أتمنى أن يكون للغات عامل تحديد نطاق يسمى "مرة واحدة" أو شيء يمكنك الخروج منه بسهولة.

إذا كنت أفعل ذلك بلغة بيرل، فهذه هي الطريقة التي يمكنني من خلالها إجراء عمليات التحقق.

{
  last unless $var1;
  last unless $var2;
  last unless $var3;
  last unless $var4;
  last unless $var5;
  last unless $var6;

  ... # Place Code Here
}

إذا كنت تخطط لاستخدام هذا عبر روتين فرعي، فاستبدل كل مثيل لـ last مع return;

أحب تقسيم كل شرط إلى متغيرات وصفية.

bool isVar1Valid, isVar2Valid, isVar3Valid, isVar4Valid;
isVar1Valid = ( var1 == 1 )
isVar2Valid = ( var2.Count >= 2 )
isVar3Valid = ( var3 != null )
isVar4Valid = ( var4 != null && var4.IsEmpty() == false )
if ( isVar1Valid && isVar2Valid && isVar3Valid && isVar4Valid ) {
     //do code
}
    if (   (condition_A)
        && (condition_B)
        && (condition_C)
        && (condition_D)
        && (condition_E)
        && (condition_F)
       )
    {
       ...
    }

في مقابل

    if (condition_A) {
       if (condition_B) {
          if (condition_C) {
             if (condition_D) {
                if (condition_E) {
                   if (condition_F) {
                      ...
                   }
                }
             }
          }
       }
    }

و

    if (   (   (condition_A)
            && (condition_B)
           )
        || (   (condition_C)
            && (condition_D)
           )
        || (   (condition_E)
            && (condition_F)
           )
       )
    {
       do_this_same_thing();
    }

في مقابل

    if (condition_A && condition_B) {
       do_this_same_thing();
    }
    if (condition_C && (condition_D) {
       do_this_same_thing();
    }
    if (condition_E && condition_F) {
       do_this_same_thing();
    }

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

تعد المحاذاة الرأسية على نفس مستوى المسافة البادئة للأقواس المفتوحة/المغلقة {}، والأقواس المفتوحة () والتعبيرات الشرطية ذات الأقواس وعوامل التشغيل على اليسار ممارسة مفيدة للغاية، مما يعزز بشكل كبير إمكانية قراءة التعليمات البرمجية ووضوحها بدلاً من تشويش كل شيء يمكن حشرها في سطر واحد، بدون محاذاة رأسية أو مسافات أو أقواس

قواعد أسبقية عامل التشغيل صعبة، على سبيل المثال.&& لها أسبقية أعلى من || ، ولكن | له الأسبقية من &&

لذا، ...

    if (expr_A & expr_B || expr_C | expr_D & expr_E || expr_E && expr_F & expr_G || expr_H {
    }

هو تعبير شرطي متعدد سهل للغاية يمكن للبشر قراءته وتقييمه بشكل غير صحيح.

    if (   (  (expr_A)
            & (expr_B)
           )
        || (  (expr_C)
            | (  (expr_D)
               & (expr_E)
              )
           )
        || (   (expr_E)
            && (  (expr_F)
                & (expr_G)
               )
           )
        || (expr_H)
       )
    {
    }

لا حرج في المسافة الأفقية (موجزات الأسطر)، أو المحاذاة الرأسية، أو الأقواس الصريحة التي توجه تقييم التعبير، وكلها تعمل على تحسين إمكانية القراءة والوضوح

إذا قمت بذلك:

if (var1 == true) {
    if (var2 == true) {
        if (var3 == true) {
            ...
        }
    }
}

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top