Вообщем есть мнение (не только мое), что применение break и continue в циклах — не очень хорошо. Во-первых — всегда можно обойтись без них, во-вторых — применение их рушит цепочку действий и ветвлений в циклах, что очень часто приводит к ошибкам.
Был цикл, который сортировал объекты по двум листам.
for (Entity e : entities) {
if (e.isApproved()) {
approvedEntities.add(e);
} else {
pendingEntities.add(e);
}
}
Потом сюда вносят исправление, так чтобы сортировка добавляла в список утвержденных те, которые валидны:
for (Entity e : entities) {
if (e.isValid()) {
approvedEntities.add(e);
continue;
}
if (e.isApproved()) {
approvedEntities.add(e);
} else {
pendingEntities.add(e);
}
}
В данном примере сразу видно где ошибка, но зачастую этих проверок и сортировок бывает много и они громоздкие — поэтому вероятность именно такой ошибки очень велика.
К тому-же, в комбинации с метками они запутывают код:
loop:
while (true) {
System.out.println("1");
break loop;
}
System.out.println("2");
При беглом просмотре может, что тут бесконечный цикл. Но на самом деле бесконечным он станет если поменять break на continue.
Вообщем, я лично не использую и не рекомендую использовать эти операторы в циклах. Хотелось бы услышать и более другие мнения по этому поводу.
Кросспост из ru_java
Здравствуйте, aefimov, Вы писали:
A>Вообщем есть мнение (не только мое), что применение break и continue в циклах — не очень хорошо. Во-первых — всегда можно обойтись без них, во-вторых — применение их рушит цепочку действий и ветвлений в циклах, что очень часто приводит к ошибкам.
Дэйкстрой попахивает. Я против уважаемого ничего не имею, даже может наоборот, но..... Когда он жил и какое время сейчас? Я говорю о развитии языков. В то время ввиду особенностей реализаци безусловного перехода в старых языках это самое использование могло привести к большим проблемам (потеря контекста или как там еще). Что касается java, то применение break (иногда вместо goto), continue с точки зрения целостности программы совсем не опасно. Остается одна проблема — читабельности кода, которое является понятием чисто субъективным. Для кого-то удобно читать цепочки ифов, запоминать семантику временных переменных, 3-5 сложное условие выхода из цикла. Для меня лично удобнее читать небольшие по размеру методы последовательно анализируя проиходящее в них. И если в какой-то точке становится понятно что дальнейшее выполнение цикла может быть прервано — почему бы не превать его в этой точке?
A>Был цикл, который сортировал объекты по двум листам. A>
A> поскипал
A>
A>Потом сюда вносят исправление, так чтобы сортировка добавляла в список утвержденных те, которые валидны: A>
A> поскипал
A>
Ну так налицо проблема с руками у кодера. Вероятно человек просто не знал что в java можно использовать конструкцию else if.
A>В данном примере сразу видно где ошибка, но зачастую этих проверок и сортировок бывает много и они громоздкие — поэтому вероятность именно такой ошибки очень велика.
Вот и я говорю, что если методы делать не большими (в идеале 10-15 строчек) то будет абсолютно эквипенисуально, используются там continue и break или нет.
A>Вообщем, я лично не использую и не рекомендую использовать эти операторы в циклах. Хотелось бы услышать и более другие мнения по этому поводу.
Я лично рекомендую пользоваться всем, но пользоваться с умом.
Здравствуйте, Lucker, Вы писали:
L>Дэйкстрой попахивает. Я против уважаемого ничего не имею, даже может наоборот, но..... Когда он жил и какое время сейчас? Я говорю о развитии языков. В то время ввиду особенностей реализаци безусловного перехода в старых языках это самое использование могло привести к большим проблемам (потеря контекста или как там еще). Что касается java, то применение break (иногда вместо goto), continue с точки зрения целостности программы совсем не опасно. Остается одна проблема — читабельности кода, которое является понятием чисто субъективным. Для кого-то удобно читать цепочки ифов, запоминать семантику временных переменных, 3-5 сложное условие выхода из цикла. Для меня лично удобнее читать небольшие по размеру методы последовательно анализируя проиходящее в них. И если в какой-то точке становится понятно что дальнейшее выполнение цикла может быть прервано — почему бы не превать его в этой точке?
Вот в том то и дело, чтобы максимально упростить код — нужно максимально просто его представить — бинарное дерево переходов помойму самое хорошее средство простого представления. Либо то, либо другое. А с breakами и continuями появляется еще третье ветвление, потом четвертое и так далее. И если с кодом работают N человек то они N раз наступят на одни и теже грабли
Ты еще упоменул про break вместо goto. Реально в каких случаях это может использоваться? Не зря ведь запретили goto в Java...
L>Ну так налицо проблема с руками у кодера. Вероятно человек просто не знал что в java можно использовать конструкцию else if.
Дык кудаж без них — хочется просто максимально убрать возможность допустить такие вот ошибки.
L>Я лично рекомендую пользоваться всем, но пользоваться с умом.
Дипломатично
Здравствуйте, aefimov, Вы писали:
A>Вообщем, я лично не использую и не рекомендую использовать эти операторы в циклах. Хотелось бы услышать и более другие мнения по этому поводу.
в догонку. Можете меня запинать, но в контексте использовнаия legacy фреймворка (где не доступен автобиндинг) не нашел более "праильного" решения
final String status;
error:
{
final int id;
try {
id = RequestUtils.getIntParameter(request, "id");
} catch (InvalidParameterException e) {
status = "Invalid id value";
break error;
}
final String name;
try {
name = RequestUtils.getStringParameter(request, "name");
} catch (InvalidParameterException e) {
status = "Invalid name value";
break error;
}
try {
Currency.getInstance(name);
} catch (IllegalArgumentException e) {
status = "Unknown currency name";
break error;
}
if (dao.saveRegistryValue(new RegistryValue(id, name))) {
status = "New Currency created successfull";
} else {
status = "New Currency updated successfull";
}
}
return list(request, response).addObject("status", status);
я конечно понимаю, что все что в error:{ } можно вынести в отдельный метод возвращающий String, но IMHO приведенный код тоже не плохо читается и -один метод.
Здравствуйте, aefimov, Вы писали:
A>Вообщем есть мнение (не только мое), что применение break и continue в циклах — не очень хорошо. Во-первых — всегда можно обойтись без них, во-вторых — применение их рушит цепочку действий и ветвлений в циклах, что очень часто приводит к ошибкам.
а еще бывают люди, которые считают, что return должен быть только в одном месте метода, лучше — в его последней строке, оправдывая это тем, что сложно разбираться в методе, который может возвращать результаты из разных своих частей. они для меня как лишнее доказательство, что все хорошо в меру, т.к. я неоднократно видел, чего стоит "таскать за собой" возвращаемое значение, пропуская оставшиеся этапы обработки, которые уже не нужно выполнять.
то же самое касается циклов. лично я предпочитаю руководствоваться другим правилом: "в каждой строке исходного текста метода длина абзацного отступа должна быть по возможности минимальной", т.е. если возможно, я стараюсь избегать ifов, с телами длиннее одной строки (а одну строку я пишу там же, где и if). если break/continue мне в этом помогают, то я смело их использую.
но в целом, доводится мне их использовать довольно редко. можно посмотреть с другой стороны: возможно, частое появление break/continue в коде стоит считать признаком неиспользования каких-то паттернов ООП. но всерьез я как-то не задумывался никогда над этим
A>Потом сюда вносят исправление, так чтобы сортировка добавляла в список утвержденных те, которые валидны: A>В данном примере сразу видно где ошибка, но зачастую этих проверок и сортировок бывает много и они громоздкие — поэтому вероятность именно такой ошибки очень велика.
я вот, кстати, не понял, где ошибка. точнее, не понял, какова на самом деле была цель исправления
я бы изначально этот код по-другому бы писал, причем одной строкой (предполагается, что approved/pendingEntities имеют одинаковый тип, например, Set или List или Collection, в противном случае потребуется явное приведение к супертипу):
for (Entity e : entities) (e.isApproved() ? approvedEntities : pendingEntities).add(e);
более того, скорее всего, критерий я бы выделил в отдельный класс:
for (Entity e : entities) (someCriteriaAlgorithm.isApproved(e) ? approvedEntities : pendingEntities).add(e);
в такой ситуации тот, кому бы потребовалось изменить критерий, исправлял бы только критерий!
т.е. с моей точки зрения этот пример к теме break/continue отношения не имеет, а имеет отношение к тому, как наглядно и правильно разделять части алгоритмов
A>К тому-же, в комбинации с метками они запутывают код:
Здравствуйте, aefimov, Вы писали:
A>Здравствуйте, NotGonnaGetUs, Вы писали:
NGG>>В таких ^^^ сам бог велел
A>Обычно внутренние циклы бъются на отдельные методы. И это тоже преабразуется к небрейковым переходам
ну если повезло и внутри вложенного for нет 5 перменных, которых которые могут изменить своё значение, то да — можно вытащить метод, добавить флаговую переменную или доп условие выхода в верхний for — и break не понадобиться.
но жизнь-то разные сюрпризы преподносит
int a=...;
int b=...;
int c=...;
m1:
for(int i=0; i<100; i++) {
int d = 0;
for(int j=0; j<100; j++) {
if ((a+i) % (b+j) = c) {
a = 3*a;
b = 4*i;
c = 2+j;
}
if (b*c % a == 0) {
break m1;
}
b++;
d = (a + b) % c;
if (a*b % с == 0) {
break;
}
}
if (d = 0) sout("yyy");
}
Как насчёт поупражняться в рефакторинге такого кода?
Здравствуйте, C0s, Вы писали:
C0s>а еще бывают люди, которые считают, что return должен быть только в одном месте метода, лучше — в его последней строке, оправдывая это тем, что сложно разбираться в методе, который может возвращать результаты из разных своих частей. они для меня как лишнее доказательство, что все хорошо в меру, т.к. я неоднократно видел, чего стоит "таскать за собой" возвращаемое значение, пропуская оставшиеся этапы обработки, которые уже не нужно выполнять.
Ну есть такое, и это кстати тоже может приводить к ошибкам, однако там компилятор не даст скомпилить код в котором промахнуться с блоком и вставят его после return.
C0s>то же самое касается циклов. лично я предпочитаю руководствоваться другим правилом: "в каждой строке исходного текста метода длина абзацного отступа должна быть по возможности минимальной", т.е. если возможно, я стараюсь избегать ifов, с телами длиннее одной строки (а одну строку я пишу там же, где и if). если break/continue мне в этом помогают, то я смело их использую.
Тут я с вами не соглашусь, потому что простота написания кода (компактность) — это совсем не то, что написание простого кода (понятность).
А ifы без скобок я вообще ненавижу Потому что было уже:
И когда в запарке закоментарили тело try, чтобы небыло там чегото, закомитили. Потом вяснили что не компилится — потому что try пустой и не кидает Exception — закоментарили весь try-catch. В результате телом ifа стал следующий statement. Эту ошибку уже сразу не обнаружили, а когда обнаружили было совсем поздно. Поэтому теперь у меня скобки форсируются в Code Style.
C0s>но в целом, доводится мне их использовать довольно редко. можно посмотреть с другой стороны: возможно, частое появление break/continue в коде стоит считать признаком неиспользования каких-то паттернов ООП. но всерьез я как-то не задумывался никогда над этим
Да, именно. И старание не пользоваться ими, помойму сподвигает думать в нужном направлении
C0s>я вот, кстати, не понял, где ошибка. точнее, не понял, какова на самом деле была цель исправления
Ошибка в том, что особо не заморачиваясь поставили проверку до всяких там других проверок и сделали что хотели, нисколько не заботясь о том, что там творится дальше.
C0s>я бы изначально этот код по-другому бы писал
Нам только дай время и денег — переписали бы все что угодно. Но legacy rод — это legacy код и приходится придерживаться некоторых правил, дабы не разрушить его совсем.
Здравствуйте, aefimov, Вы писали:
A>Вообщем есть мнение (не только мое), что применение break и continue в циклах — не очень хорошо. Во-первых — всегда можно обойтись без них, во-вторых — применение их рушит цепочку действий и ветвлений в циклах, что очень часто приводит к ошибкам.
Мое мнение и да и нет. Все хорошо в меру и к месту. У меня множество break и continue в циклах случаются, как правило в местах где производительность кода критична, а работа происходит над 8 — 20 переменными в плотной связке.(только ненадо предлагать их передавать упаковав их в объект — производительность критична )
А причиной тому, как правило то, что JAVA не позволяет передавать простые типы по ссылке
Согласен, что обилие меток, а так же break и continue это несколько усложняет чтение кода. Но так чтобы это приводило к сложно отлавливаемым ошибкам такого никогда не случалось.
Здравствуйте, Lucker, Вы писали:
L>в догонку. Можете меня запинать, но в контексте использовнаия legacy фреймворка (где не доступен автобиндинг) не нашел более "праильного" решения L>я конечно понимаю, что все что в error:{ } можно вынести в отдельный метод возвращающий String, но IMHO приведенный код тоже не плохо читается и -один метод.
да зачем запинывать-то ... я бы действительно сделал отдельный метод, но бросающий исключение с текстом в случае неполадок и возвращающий boolean как флаг create или update. но это не более, чем дело привычки, судя по всему. с моей точки зрения я предпочту свой вариант, но при этом признаю, что оба одинаково читабельны
Здравствуйте, Lucker, Вы писали:
L>Здравствуйте, aefimov, Вы писали:
A>>Вообщем, я лично не использую и не рекомендую использовать эти операторы в циклах. Хотелось бы услышать и более другие мнения по этому поводу. L>в догонку. Можете меня запинать, но в контексте использовнаия legacy фреймворка (где не доступен автобиндинг) не нашел более "праильного" решения
Да все пучком.
При желании Спагети-код можно и безо всяких меток, break и continue наколбасить.
И наоборот именно метоки, break и continue могут сделать код ВЕСЬМА читаемым.(ваш пример)
Так что несогласен я. Руки прочь от break и continue!
Здравствуйте, NotGonnaGetUs, Вы писали:
NGG>>Как насчёт поупражняться в рефакторинге такого кода? NGG>'=' в условиях читать как '==' %)
Да там и буковка c русская Ладно, вот чего получилось:
public class Test {
private int a;
private int b;
private int c;
public Test(int a, int b, int c) {
this.a = a;
this.b = b;
this.c = c;
}
public int get(int i) {
int d = 0;
for (int j = 0;
j == 0 || j < 100 && a * b % c == 0;
d = (a + ++b) % c) {
calc(i, j++);
}
return d;
}
private void calc(int i, int j) {
if ((a + i) % (b + j) == c) {
a = 3 * a;
b = 4 * i;
c = 2 + j;
}
if (b * c % a == 0) {
throw new IllegalArgumentException("i = " + i + ", j = " + j);
}
}
private static void foo(int a, int b, int c) {
Test test = new Test(a, b, c);
try {
for (int i = 0; i < 100; i++) {
if (test.get(i) == 0) {
System.out.println("yyy");
}
}
} catch (IllegalArgumentException e) {
// Ignore...
}
}
}
Здравствуйте, vladserge, Вы писали:
V>Мое мнение и да и нет. Все хорошо в меру и к месту. У меня множество break и continue в циклах случаются, как правило в местах где производительность кода критична, а работа происходит над 8 — 20 переменными в плотной связке.(только ненадо предлагать их передавать упаковав их в объект — производительность критична )
А в чем падение производительности?
Здравствуйте, Lucker, Вы писали:
L>в догонку. Можете меня запинать, но в контексте использовнаия legacy фреймворка (где не доступен автобиндинг) не нашел более "праильного" решения
Здравствуйте, aefimov, Вы писали:
A>Здравствуйте, vladserge, Вы писали:
V>>Мое мнение и да и нет. Все хорошо в меру и к месту. У меня множество break и continue в циклах случаются, как правило в местах где производительность кода критична, а работа происходит над 8 — 20 переменными в плотной связке.(только ненадо предлагать их передавать упаковав их в объект — производительность критична ) A>А в чем падение производительности?
ок, представте себе статический метод который может ЧАСТО вызываться несколькими потоками в теле метода создаются 10-20 взаимосвязанных простых переменных алгоритм достаточно сложен.
Проверка показала, что время требуемое на создание объекта примерно равно(чуть меньше) скорости выполнения метода.
Но зато потом создание таких обектов приводит к более частому запуску GC.
Здравствуйте, aefimov, Вы писали:
A>Здравствуйте, NotGonnaGetUs, Вы писали:
NGG>>>Как насчёт поупражняться в рефакторинге такого кода? NGG>>'=' в условиях читать как '==' %)
A>Да там и буковка c русская Ладно, вот чего получилось: A>
A>public class Test {
A> private int a;
A> private int b;
A> private int c;
A> public Test(int a, int b, int c) {
A> this.a = a;
A> this.b = b;
A> this.c = c;
A> }
A> public int get(int i) {
A> int d = 0;
A> for (int j = 0;
A> j == 0 || j < 100 && a * b % c == 0;
A> d = (a + ++b) % c) {
A> calc(i, j++);
A> }
A> return d;
A> }
A> private void calc(int i, int j) {
A> if ((a + i) % (b + j) == c) {
A> a = 3 * a;
A> b = 4 * i;
A> c = 2 + j;
A> }
A> if (b * c % a == 0) {
A> throw new IllegalArgumentException("i = " + i + ", j = " + j);
A> }
A> }
A> private static void foo(int a, int b, int c) {
A> Test test = new Test(a, b, c);
A> try {
A> for (int i = 0; i < 100; i++) {
A> if (test.get(i) == 0) {
A> System.out.println("yyy");
A> }
A> }
A> } catch (IllegalArgumentException e) {
A> // Ignore...
A> }
A> }
A>}
A>
И что, Вы серьезно полагаете, что наводненние Вашего кода вот такими классами и методами (разумное-то навзвания которым затруднительно придумать calc1, calc2, calc3, get1, get2.... ) упростит понимание кода?