Система не должна отказывать, и приложение не должно падать. Эта мантра широко применяется при разработке программного обеспечения и проектировании распределенных систем. Различные компании внедрили какие-то методы для предотвращения сбоев своего приложения. Например, Amazon защищает свою облачную инфраструктуру от сбоев, используя систему высокой доступности, которая относится к системе без единой точки отказа - путем создания избыточности, чтобы в случае отказа одного из компонентов система могла продолжать работать.

Facebook использует разбиение своей базы данных MYSQL на каждый регион при первом запуске, чтобы предотвратить сбой своего приложения. Однако, как и все люди, совершающие ошибки, вся система столкнется с ошибками.

Существуют различные способы ограничить работу приложения, чтобы избежать сбоев, например, отложить сбой, найти обходное решение для ошибок, чтобы система могла продолжать работать, и просто потерпел неудачу. Эти методы - не лучший способ справиться с ошибкой системы.

Почему? Потому что выполнение этих трех вещей не решило проблему. Ошибка все еще существует. Скрытие проблемы только приведет к тому, что в будущем проблема станет еще больше, и для ее устранения потребуется больше времени и ресурсов.

Итак, что нам делать, если приложение не работает?

Ответ - быстро потерпеть неудачу, а затем вернуться пораньше. Нам нужно изменить подход к разработке программного обеспечения. Мы должны выдать исключение и вернуть его, если есть какая-либо проблема, вместо того, чтобы избегать каких-либо исключений в методе и возвращаться изящно.

«Самым неприятным аспектом разработки программного обеспечения для меня является отладка. Ошибки, которые я ненавижу, проявляются после успешной ежечасной операции при необычных обстоятельствах ». - Джим Шор

Быстрый отказ вместо надежного

Если программное обеспечение выйдет из строя быстро, это не уменьшит количество ошибок в системе; но будет легче поймать ошибку и исправить проблему. Быстрый отказ побудит нас немедленно и заметно вывести систему из строя. Казалось, что это сделает нашу систему хрупкой, если сразу обнаружит ошибку.

Однако он создает более надежную систему, которая устраняет проблему до того, как она поступит в производство. Когда ошибки легче обнаружить и воспроизвести, их легче исправить. Например, давайте создадим безотказный метод расчета размера арендной платы:

public int maxRent() {
  RentPortfolio rent = Portfolio.get("rent-portfolio");
  if(rent == null) {
    return 1000;
  } 
  return rent.getMaxPrice();
} 

Этот метод рассчитывает максимальную арендную плату, получая свойство rent-portfolio от объекта Portfolio. Когда вы вводите любое значение в этом методе, оно корректно возвращается.

Теперь представьте, что в классе Portfolio произошло незначительное обновление, когда один разработчик, обновляющий кодовую базу, допустил единственную опечатку с rent-portfolio на rent-porttfolio.

Программное обеспечение по-прежнему будет успешно доставлено, и приложение по-прежнему будет работать. Однако пользователь будет каждый раз получать неправильное значение, поскольку он вернет значение по умолчанию. Когда клиент понял, что это неточно, будет сложно отладить проблему.

Вместо этого, если вы напишете безотказный метод:

public int maxRent() {
  RentPortfolio rent = Portfolio.get("rent-portfolio");
  if(rent == null) {
    throw new PortfolioNotFoundException("rent-portfolio is not found in " + this.portfolioPath);
  }
  return rent.getMaxPrice();
}

На этот раз разработчик обнаружит ошибку перед отправкой программного обеспечения, поскольку она выдает PortfolioNotFoundException.

Другой пример отказоустойчивого и отказоустойчивого метода:

Отказоустойчивый

public division(int numerator, int denominator) {
  /* some code here ........ */
  return numerator / denominator;
}

public static void main(String...args) {
  division(2,0);
}

Exception: java.lang.ArithmeticException: / by zero …

Безотказно

public division(int numerator, int denominator) {
  if(denominator == 0) throw new IllegalArgumentException("denominator cannot be 0");
  /* some code here ..... */
  return numerator / denominator;
}

public static void main(String...args) {
  division(2,0);
}

Exception: java.lang.IllegalArgumentException: denominator cannot be 0 ….

В этом примере отказоустойчивый вызов вызывает ошибку. Однако приложение выдает неправильное сообщение. Основная причина ошибки в том, что это InvalidArgumentException вместо ArithmeticException. Таким образом, разработчику необходимо несколько раз перебрать код, чтобы узнать, в чем проблема. Fail-fast поможет разработчику, обслуживающему программу, немедленно обнаружить ошибку.

Советы - пара Fails Fast проверок:

  • Нулевой
  • Пустой строки
  • Пустой список
  • Большинство предварительных проверок

Способы выявления видимого отказа:

  • Использование разработки через тестирование. вы сможете указать успешный вариант использования и неудачный вариант использования с помощью утверждения еще до того, как вы реализуете свой метод.
  • Наличие сборки с непрерывной интеграцией в процессе разработки программного обеспечения гарантирует любой необходимый сбой при слиянии функциональной ветки с основной веткой.
  • Создание утверждения для проверки работоспособности - разницу между утверждением и исключением можно найти здесь (ссылка).
  • Когда возникает какая-либо проблема с настройкой системы, вместо функции отката отказал система, чтобы вы могли быть уведомлены и немедленно устранить проблему.
  • Когда интерфейс запрашивает GET-вызов для коллекции на сервере, вместо того, чтобы возвращать пустой список, когда он не найден, возвращайте исключение 404 not found.

Вернуться раньше

Раннее возвращение идет рука об руку с провальным постом. Обнаружив ошибку, вы можете либо выбросить ее в системный стек над функцией, либо немедленно вернуть эту ошибку. Например, если вы хотите создать предварительные проверки в параметрах функции, вы можете сделать что-то вроде этого:

public String getPersonalData(Person person) {
  if(!systemIsUp) {
   if(person.getName() == "") {
    if(person.age == 0) {
      if(person.pin == "") {
        return "Person doesn't have a pin"; 
      }
      return "Person age cannot be 0";
    }
    return "Person Doesn't have a name"
   }
   return "System is Down"
  }
  return person.getName();
}

Если вы читаете этот код, к тому времени, когда вы дойдете до третьего оператора, вы можете забыть, каким должен быть оператор из двух предыдущих аргументов. Основная проблема с этой проверкой заключается в том, что ее трудно не только отлаживать, когда что-то выходит из строя, но и трудно читать.

Это приводит к высокой цикломатической сложности. Цикломатическая сложность - это метрика, которая измеряет, насколько сложна ваша программная система. Как измерить сложность? Когда есть if, else, try, catch, for, and, or, others, while, вы добавляете немного сложности. Более высокая цикломатическая сложность означает, что система сложна и трудна в обслуживании.

Кроме того, трудно сохранить в памяти все проверки и вложенный оператор if / else. Вместо этого верните заранее подготовленный оператор if / else, начиная с простого условия.

public String getPersonal(Person person) {
  if(!systemIsUp) return "System is Down";
  
  if(person.getName() == "") return "Person Doesn't have a name";
  
  if(person.age == 0) return "Person age cannot be 0";
  
  if(person.pin == 0) return "Person' doesn't have a pin";
  
  return person.getName();
}

Если вы читаете этот код, вы поймете, каким должно быть предварительное условие. Вам не нужно иметь лист бумаги и писать все операторы if / else, когда вы отлаживаете приложение. Кроме того, в функции написано меньше кода.

Выводы

«Неудачи, повторяющиеся неудачи - это пальцы на пути к достижению». Один терпит неудачу к успеху ». К.С. Льюис.

Чтобы сделать систему более надежной, разработчик должен устранять любые возникающие ошибки, чтобы их можно было легко обнаружить и немедленно исправить. Хороший способ справиться с предусловием и проверкой работоспособности - это выбросить исключение или утверждение. Если условие не выполнено, немедленно верните его. Благодаря быстрому отказу и раннему возврату мы можем снизить затраты на отладку и улучшить качество системы.

Спасибо за прочтение! Если вам понравился этот пост, не стесняйтесь подписаться на мою рассылку, чтобы получать еженедельные эссе о карьере в технологиях, интересные ссылки и контент каждую неделю!

Как сохранить чистый код в вашем проекте

Вы также можете подписаться на меня на Medium, чтобы увидеть больше подобных сообщений.

Первоначально опубликовано на https://edward-huang.com.

Ссылка:

Другие полезные ресурсы для изучения Java, которые могут вам понравиться
10 вещей, которые Java-программист должен выучить в 2019 году
10 бесплатных курсов для изучения Java с нуля
10 Книги для углубленного изучения Java
10 инструментов, которые должен знать каждый Java-разработчик
10 причин изучать языки программирования Java
10 фреймворков, которые Java и веб-разработчики должны изучить в 2019 году < br /> 10 советов, как стать лучшим Java-разработчиком в 2019 году
5 лучших Java-фреймворков, которые следует изучить в 2019 году
10 библиотек тестирования, которые должен знать каждый Java-разработчик