Рассмотрим следующий код:
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
Конструкция также полезна для этих видов петель , так как они не должны быть «загрунтовать» .