Один из наиболее распространённых неудачных подходов — это использование исключений для контроля потока выполнения. Этого следует избегать по двум причинам:
- Это снижает производительность и быстродействие вашего кода
- Это делает ваш код менее читаемым
Давайте начнём с рассмотрения примера. Здесь исключение используется для управления потоком выполнения:
public static int findAge(String name) { try { String ageAsString = findUser(name); return ageAsString.length(); } catch (NameNotFoundException e) { return 0; } } private static String findUser(String name) { if(name==null) { throw new NameNotFoundException(); } return name; }
Если пользователь предоставит не нулевое значение имени, то метод findAge вернёт длину этого имени, но если имя пользователя будет null, то тогда метод findUser выбросит исключение NameNotFoundException и в этом случае метод findAge вернёт 0.
Как мы можем преобразовать этот код без использования исключений? Вообще, существует множество вариантов, рассмотрим один из них:
public static int findAgeNoEx(String name) { String ageAsString = findUserNoEx(name); return ageAsString.length(); } private static String findUserNoEx(String name) { if(name==null) { return ""; } return name; }
Чтобы выяснить влияние исключения на производительность, я подготовил следующий код, который вызывает 10 миллионов раз оба варианта методов: с исключением и без.
public class ControlFlowWithExceptionOrNot { public static class NameNotFoundException extends RuntimeException { private static final long serialVersionUID = 3L; } private static final int TRIAL = 10000000; public static void main(String[] args) throws InterruptedException { long start = System.currentTimeMillis(); for (int i = 0; i < TRIAL; i++) { findAgeNoEx(null); } System.out.println("Duration :" + (System.currentTimeMillis() - start)); long start2 = System.currentTimeMillis(); for (int i = 0; i < TRIAL; i++) { findAge(null); } System.out.println("Duration :" + (System.currentTimeMillis() - start2)); }; public static int findAge(String name) { try { String ageAsString = findUser(name); return ageAsString.length(); } catch (NameNotFoundException e) { return 0; } } private static String findUser(String name) { if (name == null) { throw new NameNotFoundException(); } return name; } public static int findAgeNoEx(String name) { String ageAsString = findUserNoEx(name); return ageAsString.length(); } private static String findUserNoEx(String name) { if (name == null) { return ""; } return name; } }
Вывод:
Duration :16 Duration :6212
Как видно, использование исключения обошлось нам в тысячи миллисекунд на моём Intel Core i7-3630QM.
Если мы сравним два наших findAge метода с точки зрения читабельности, то метод без исключения абсолютно понятен: во-первых мы может быть абсолютно уверены, что метод findUser возвращает строку; а во-вторых независимо от того, какая именно строка будет возвращена, мы получим её длину. В то же время, метод с исключением несколько запутанный: не до конца ясно что именно возвращает метод findUser. Он может вернуть строку, а может и выбросить исключение и из сигнатуры метода этого не видно. По этой причине парадигма функционального программирования не приветствует использование исключений.
В конце концов, будет лучше, если вы будете использовать исключения там, где действительно возникает исключительная ситуация и это необходимо. Если же вы будете использовать исключения для управления потоком выполнения, это вызовет падение производительности программы и сделает ваш код менее читабельным.
Надеюсь, данная статья была вам интересна, а возможно и полезна.
ссылка на оригинал статьи https://habr.com/ru/post/476142/
Добавить комментарий