Чистый код. Создание, анализ и рефакторинг (Мартин) - страница 84

>    throw new DeviceShutDownError("Invalid handle for: " + id.toString());

>    ...

>  }


>  ...

>}

Начните с написания команды  try-catch-finally

У исключений есть одна интересная особенность: они определяют область видимости в вашей программе. Размещая код в секции try команды try-catch-finally, вы утверждаете, что выполнение программы может прерваться в любой точке, а затем продолжиться в секции catch.

Блоки try в каком-то отношении напоминают транзакции. Секция catch должна оставить программу в целостном состоянии, что бы и произошло в секции try. По этой причине написание кода, который может инициировать исключения, рекомендуется начинать с конструкции try-catch-finally. Это поможет вам определить, чего должен ожидать пользователь кода, что бы ни произошло в коде try.

Допустим, требуется написать код, который открывает файл и читает из него сериализованные объекты.

Начнем с модульного теста, который проверяет, что при неудачном обращении к файлу будет выдано исключение:

>@Test(expected = StorageException.class)

>public void retrieveSectionShouldThrowOnInvalidFileName() {

>  sectionStore.retrieveSection("invalid - file");

>}

Для теста необходимо создать следующую программную заглушку:

>public List retrieveSection(String sectionName) {

>  // Пусто, пока не появится реальная реализация

>  return new ArrayList();

>}

Тест завершается неудачей, потому что код не инициирует исключения. Затем мы изменяем свою реализацию так, чтобы она попыталась обратиться к несуществующему файлу. При попытке выполнения происходит исключение:

>public List retrieveSection(String sectionName) {

>  try {

>    FileInputStream stream = new FileInputStream(sectionName)

>  } catch (Exception e) {

>    throw new StorageException("retrieval error", e);

>  }

>  return new ArrayList();

>}

Теперь тест проходит успешно, потому что мы перехватили исключение. На этой стадии можно переработать код. Тип перехватываемого исключения сужается до типа, реально инициируемого конструктором FileInputStream, то есть FileNotFoundException:

>public List retrieveSection(String sectionName) {

>  try {

>    FileInputStream stream = new FileInputStream(sectionName);

>    stream.close();

>  } catch (FileNotFoundException e) {

>    throw new StorageException("retrieval error", e);

>  }

>  return new ArrayList();

>}

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