Розглянемо наступний код:
public void doSomething(int input)
{
while(true)
{
TransformInSomeWay(input);
if(ProcessingComplete(input))
break;
DoSomethingElseTo(input);
}
}
Припустимо, що цей процес включає кінцеву, але залежну від входу кількість кроків; цикл призначений для завершення самостійно в результаті алгоритму і не розрахований на невизначений час (поки його не скасує зовнішня подія). Оскільки тест на перевірку того, чи повинен цикл закінчуватися, знаходиться посередині логічного набору кроків, а сам цикл while в даний час не перевіряє нічого значимого; перевірка замість цього виконується в "належному" місці в рамках концептуального алгоритму.
Мені сказали, що це поганий код, оскільки він більш схильний до помилок через те, що закінчується не перевіряється структурою циклу. Складніше розібратися, як ви виходите з циклу, і можете запросити помилки, оскільки умова порушення може бути обійдений або опущений випадково з урахуванням майбутніх змін.
Тепер код можна структурувати так:
public void doSomething(int input)
{
TransformInSomeWay(input);
while(!ProcessingComplete(input))
{
DoSomethingElseTo(input);
TransformInSomeWay(input);
}
}
Однак це дублює виклик методу в коді, порушуючи DRY; якби TransformInSomeWay
згодом були замінені якимись іншими методами, обидва виклики потрібно було б знайти та змінити (а той факт, що в більш складному фрагменті коду може бути менш очевидним).
Ви також можете написати це так:
public void doSomething(int input)
{
var complete = false;
while(!complete)
{
TransformInSomeWay(input);
complete = ProcessingComplete(input);
if(!complete)
{
DoSomethingElseTo(input);
}
}
}
... але тепер у вас є змінна, єдина мета якої - перенести перевірку стану на структуру циклу, а також має бути перевірена кілька разів, щоб забезпечити таку саму поведінку, як і вихідна логіка.
Зі свого боку, я кажу, що зважаючи на алгоритм, який цей код реалізує в реальному світі, оригінальний код є найбільш читабельним. Якби ви переглядали це самостійно, це так, як ви думали б про це, і тому було б інтуїтивно зрозумілим людям, знайомим з алгоритмом.
Отже, що "краще"? чи краще надати відповідальність за перевірку умови циклу while, структуруючи логіку навколо циклу? Або краще структурувати логіку "природним" способом, як це вказується вимогами чи концептуальним описом алгоритму, навіть якщо це може означати обхід вбудованих можливостей циклу?
do ... until
Конструкція також корисна для цих видів петель , так як вони не повинні бути «заґрунтувати» .