Rozważ następujący kod:
public void doSomething(int input)
{
while(true)
{
TransformInSomeWay(input);
if(ProcessingComplete(input))
break;
DoSomethingElseTo(input);
}
}
Załóżmy, że proces ten obejmuje skończoną, ale zależną od danych wejściowych liczbę kroków; pętla została zaprojektowana tak, aby zakończyć się sama w wyniku działania algorytmu i nie jest zaprojektowana do działania w nieskończoność (dopóki nie zostanie anulowana przez zdarzenie zewnętrzne). Ponieważ test sprawdzający, czy pętla powinna się zakończyć, znajduje się w środku logicznego zestawu kroków, sama pętla while obecnie nie sprawdza niczego znaczącego; zamiast tego sprawdzanie odbywa się w „właściwym” miejscu algorytmu koncepcyjnego.
Powiedziano mi, że jest to zły kod, ponieważ jest bardziej podatny na błędy ze względu na to, że warunek końcowy nie jest sprawdzany przez strukturę pętli. Trudniej jest wymyślić, jak wyjść z pętli, i może zapraszać błędy, ponieważ warunek przerwania może zostać ominięty lub pominięty przypadkowo, biorąc pod uwagę przyszłe zmiany.
Teraz kod może mieć następującą strukturę:
public void doSomething(int input)
{
TransformInSomeWay(input);
while(!ProcessingComplete(input))
{
DoSomethingElseTo(input);
TransformInSomeWay(input);
}
}
Powoduje to jednak duplikowanie wywołania metody w kodzie, co narusza DRY; gdyby TransformInSomeWay
zostały później zastąpione inną metodą, oba wywołania musiałyby zostać znalezione i zmienione (a fakt, że są dwa, może być mniej oczywisty w bardziej złożonym fragmencie kodu).
Możesz również napisać to tak:
public void doSomething(int input)
{
var complete = false;
while(!complete)
{
TransformInSomeWay(input);
complete = ProcessingComplete(input);
if(!complete)
{
DoSomethingElseTo(input);
}
}
}
... ale masz teraz zmienną, której jedynym celem jest przeniesienie sprawdzania warunków do struktury pętli, a także musi być sprawdzana wiele razy, aby zapewnić takie samo zachowanie jak oryginalna logika.
Ze swojej strony mówię, że biorąc pod uwagę algorytm, jaki ten kod implementuje w świecie rzeczywistym, oryginalny kod jest najbardziej czytelny. Gdybyś sam to przeszedł, tak byś o tym pomyślał, dlatego byłoby to intuicyjne dla osób zaznajomionych z algorytmem.
Więc co jest „lepsze”? czy lepiej jest powierzyć odpowiedzialność za sprawdzanie stanu pętli while, konstruując logikę wokół pętli? Czy może lepiej jest ustrukturyzować logikę w „naturalny” sposób, na co wskazują wymagania lub opis koncepcyjny algorytmu, nawet jeśli może to oznaczać ominięcie wbudowanych możliwości pętli?
do ... until
Konstrukt jest również przydatna dla tych rodzajów pętli, ponieważ nie muszą być „zalane”.