Вход/Регистрация
Программист-прагматик. Путь от подмастерья к мастеру
вернуться

Хант Эндрю

Шрифт:

Рассмотрим реализацию приложения «клиент-сервер» с использованием средства RMI (удаленный вызов метода) в языке Java. Поскольку RMI реализован определенным способом, каждое обращение к удаленной подпрограмме должно быть подготовлено, с тем чтобы обработать ситуацию RemoteException. Добавление программы обработки этих исключений может представлять собой утомительную процедуру и означает сложность написания программы, которая могла бы работать как с локальными, так и с удаленными подпрограммами. Обойти эту трудность возможно путем инкапсулирования удаленных объектов в класс, не являющийся удаленным. Тогда этот класс сможет реализовать интерфейс обработчика ошибок, позволяя программе клиента регистрировать подпрограмму, обращение к. которой происходит при обнаружении удаленной исключительной ситуации.

Другие разделы, относящиеся к данной теме:

• Мертвые программы не лгут

Вопросы для обсуждения

• В языках программирования, не поддерживающих исключительные ситуации, часто используется иной (нелокальный) способ передачи механизма управления (например, в языке С существует средство longjmp/setjmp). Подумайте, как можно реализовать некий «суррогатный» механизм исключения, используя указанные средства. В чем состоят преимущества и опасности? Какие специальные меры необходимо предпринять для гарантии того, что эти ресурсы не «осиротеют»? Есть ли смысл использовать подобное решение всякий раз, когда вы пишете программу на языке С?

Упражнения

21. При проектировании нового класса контейнера имеются три возможных состояния ошибки:

1. Не хватает памяти для нового элемента в подпрограмме add.

2. В подпрограмме fetch не обнаружена запрашиваемая точка входа.

3. Указатель null передан к подпрограмме add.

Каким образом необходимо обрабатывать каждую из этих ошибок? Нужна ли генерация ошибки, возбуждение исключительной ситуации, или же это состояние должно игнорироваться? (Ответ см. в Приложении В).

25

Балансировка ресурсов

"Я привел тебя в этот мир", – сказал бы мой отец, – "я же могу и отправить тебя обратно. Мне это без разницы. Я сделаю еще одного такого, как ты".

Билл Косби, Отцовство

При написании программ всем нам приходится управлять ресурсами: памятью, транзакциями, потоками, файлами, таймерами – словом, всеми типами объектов, доступность которых ограничена. Большую часть времени использование ресурса следует предсказуемой схеме: ресурс назначается, используется, а затем освобождается.

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

Подсказка 35: Доводите до конца то, что начинаете

В большинстве случаев эту подсказку применить несложно. Она всего лишь означает, что подпрограмма или объект, который назначает ресурс, должен нести ответственность за освобождение этого ресурса. Использование указанной подсказки можно рассмотреть на примере неудачной программы – приложения, открывающего файл, считывающего из него информацию о заказчике, обновляющего поле и записывающего результат обратно в файл. Чтобы сделать пример более наглядным, мы опустили процедуру обработки ошибок.

void readCustomer(const char *fName, Customer *cRec) {

cFiie = fopen(fName, "r+");

fread(cRec, sizeof(*cRec), 1, cFile);

}

void writeCustomer(Customer *cRec) {

rewind(cFile);

fwrite(cRec, sizeof(*cRec), 1; cFile);

fclose(cFile);

}

void updateCustomer(const char *fName, double newBalance) {

Customer cRec;

readCustomer(fName, &cRec);

cRec.balance = newBalance;

writeCustomer(&cRec);

}

На первый взгляд, подпрограмма updateCustomer выглядит довольно прилично. Похоже, что она реализует нужную нам логику – считывает запись, обновляет баланс и осуществляет запись обратно в файл. Однако, за внешним приличием и скрывается главная проблема. Подпрограммы readCustomer и writeCustomer тесно связаны между собой [27] – они совместно используют глобальную переменную cFile. Подпрограмма readCustomer открывает файл и сохраняет указатель файла в переменной cFile, а подпрограмма writeCustomer использует сохраненный указатель для закрытия файла по окончании работы. Эта глобальная переменная даже не появляется в подпрограмме updateCustomer.

27

Опасности, возникающие из-за связанности в программе, обсуждаются в разделе "Несвязанность и закон Деметера".

Почему эту программу можно считать неудачной? Представим несчастную даму-программистку из службы сопровождения, которой сказали, что спецификация изменилась – баланс должен обновляться только в том случае, если его новое значение неотрицательное. Дама открывает файл с исходным текстом и изменяет подпрограмму updateCustomer:

void updateCustomer(const char *fName, double newBalance) {

Customer cRec;

readCustomer(fName, &cRec);

if (newBalance >= 0.0) {

cRec.balance = newBalance;

writeCustomer(&cRec);

}

}

Во время испытания все выглядит замечательно. Однако когда программа вводится в эксплуатацию, она «падает» через несколько часов, выдавая сообщение "Слишком много открытых файлов". Поскольку подпрограмма writeBalance в ряде случаев не вызывается, то и файл не закрывается.

У этой проблемы есть весьма неудачное решение – использовать специальный оператор выбора в подпрограмме updateCustomer:

void updateCustomer(const char *fName, double newBalance) {

  • Читать дальше
  • 1
  • ...
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: