ГЛАВА 6. НАПИСАНИЕ НАДЕЖНЫХ ПРОГРАММ.
Обработка ошибок в интерактивном пользовательском интерфейсе
намного сложнее, чем в утилитах командной строки. В неинтерактивной
программе совешенно приемлемо, что ошибки программы приводят к
выводу сообщения об ошибке и завершению программы. Однако, в
интерактивных программах необходимо восстановить состояние после
ошибки и оставить пользователя в приемлемом состоянии. Ошибки не
должны приводит к разрушению информации пользователя и не должны
завершать программу вне зависимости от их природы. Программа,
удовлетворяющая этим критериям, считается "надежной".
Turbo Vision помогает писать надежные программы. Он
поддерживает стиль программирования, который позволяет более просто
обнаружить ошибку и восстановить состояние, особенно коварную и
хитрую ошибку Out of memory. Это обеспечивается поддержкой
концепции атомарных операций.
Все или ничего.
Атомарная операция - это операция, которая не может быть
разбита на более мелкие операции. Или в данном контексте, это
операция, которая либо полностью ошибочна, либо полностью успешна.
Создание атомарных операций особенно полезно при распределении
памяти.
Обычно программа распределяет память небольшими кусочками.
Например при создании диалогового окна Вы распределяете память под
диалоговое окно, а затем под каждый элемент управления. Каждое из
этих распределений потенциально может быть неудачным и каждая
возможная ошибка требует проверки, можете ли Вы продолжать
распределение или должны остановиться. Если любое распределение
ошибочно, Вам необходимо освободить память, которая была
распределена успешно. В идеале Вы должны делать проверку после
каждого распределения. Введите пул надежности.
Пул надежности.
Turbo Vision устанавливает фиксированное количество памяти (по
умолчанию 4К) в конце кучи, называемое пулом надежности. Если
распределение памяти в куче достигает пула надежности, функция
Turbo Vision LowMemory возвращает True. Это означает, что
последующие распределения ненадежны и могут привести к ошибке.
Чтобы использование пула надежности давало эффект, пул должен
быть больше, чем максимальное атомарное распределение. Другими
словами, он должен быть достаточно большим, чтобы все распределения
между проверками LowMemory были успешными; 4К должны удовлетворять
большинство программ. (Размер пула надежности устанавливается
переменной LowMemSize).
Использование традиционного подхода при распределении памяти
создает диалоговое окно:
OK := True;
R.Assign(20,3,60,10);
D := New(Dialog, Init(R, 'My dialog'));
if D <> nil then
begin
with D^ do
begin
R.Assign(2,2,32,3);
Control := New(PStaticText, Init(R,
'Do you really wish to do this?'));
if Control <> nil then Insert(Control)
else OK := False;
R.Assign(5,5,14,7);
Control := New(PButton, Init(R, '~Y~es', cmYes));
if Control <> nil then Insert(Control)
else OK := False;
R.Assign(16,6,25,7);
Control := New(PButton, Init(R, '~N~o', cmNo));
if Control <> nil then Insert(Control)
else OK := False;
R.Assign(27,5,36,7);
Control := New(PButton, Init(R, '~C~ancel', cmCancel));
if Control <> nil then Insert(Control)
else OK := False;
end;
if not OK then Dispose(D, Done);
end;
Заметим, что переменная OK используется для указания ошибочных
распределений. Если произошла ошибка, все диалоговое окно должно
быть удалено. Вспомним, что удаление диалогового окна так же
удаляет все его подэлементы. С другой стороны, с пулом надежности
весь этот блок кода может интерпретироваться как атомарная операция
и код изменяется:
R.Assign(20,3,60,10);
D := New(Dialog, Init(R, 'My dialog'));
with D^ do
begin
R.Assign(2,2,32,3);
Insert(New(PStaticText, Init(R,
'Do you really wish to do this?')));
R.Assign(5,5,14,7);
Insert(New(PButton, Init(R, '~Y~es', cmYes)));
R.Assign(16,6,25,7);
Insert(New(PButton, Init(R, '~N~o', cmNo)));
R.Assign(27,5,36,7);
Insert(New(PButton, Init(R, '~C~ancel', cmCancel)));
end;
if LowMemory then
begin
Dispose(D, Done);
OutOfMemory;
DoIt := False;
end;
else
DoIt := DeskTop^.ExecView(D) = cmYes;
Поскольку пул надежности достаточно велик для распределения
всего диалогового окна, которое требует менее 4К, этот код может
предполагать, что все распределения успешны. После того, как
диалоговое окно полностью распределится, проверяется переменная
LowMemory, и если она True, то все диалоговое окно уничтожается;
иначе используется.
Метод ValidView.
Поскольку LowMemory проверяется достаточно часто, TApplication
имеет метод ValidView, который может вызываться для выполнения
необходимой проверки. Используя ValidView, проверка if в последних
8 строках может быть заменена на 2 строки:
DoIt := (ValidView(D) <> nil) and
(DeskTop^.ExecView(D) = cmYes;
ValidView возвращает либо указатель на переданный видимый
элемент, либо nil, если видимый элемент неверен. Если LowMemory
возвращает True, ValidView освобождает видимый элемент и вызывает
OutOfMemory.
Другие ошибки.
Конечно не все ошибки связаны с памятью. Например видимый
элемент может читать дисковый файл, а файл может быть не найден или
разрушен. Этот тип ошибки должен быть указан пользователю. К
счастью ValidView может использовать встроенную обработку ошибок,
не связанных с памятью, вызывая метод Valid видимого элемента.
TView.Valid возвращает True по умолчанию. TGroup.Valid
возвращает True только если все подэлементы, принадлежащие группе,
возвращают True из их функций Valid. Другими словами, группа
правильна, если все подэлементы этой группы правильны. Когда Вы
создаете видимый элемент, который может встретить ошибку, не
связанную с памятью, Вам необходимо перекрыть Valid для этого
видимого элемента так, чтобы он возвращал True только при успешном
создании.
Valid может использоваться для указания, что видимый элемент
не должен использоваться по какой-то причине; например, если
видимый элемент не может найти файл. Заметим, что то, ЧТО Valid
проверяет и КАК он проверяет зависит от Вас. Типичный метод Valid
имеет вид:
function TMyView.Valid(Command: Word): Boolean;
begin
Valid := True;
if Command = cmValid then
begin
if ErrorEncountered then
begin
ReportError;
Valid := False;
end;
end;
end;
Когда создается видимый элемент, метод Valid должен быть
вызван с параметром Command, равным cmValid для проверок любых
ошибок, не связанных с памятью, возникших при создании видимого
элемента. ValidView(X) вызывает Х.Valid(cmValid) автоматически, а
также проверяет пул надежности, так что вызов ValidView перед
использованием любого нового видимого элемента - это хорошая идея.
Valid так же вызывается при завершении модального состояния с
параметром Command в команде, завершающей модальное состояние. (См.
главу 4.) Это дает Вам возможность перехватывать такие условия, как
несохраненный текст в окне редактора до завершения Вашей программы.
ErrorEnсountered может быть и обычно бывает булевской
переменной экземпляра объектного типа, который указан в вызове
Init.
Сообщения об ошибках.
До того, как метод Valid вернет False, он должен выдать
пользователю информацию об ошибке, поскольку видимый элемент не
появится на экране. Это делал ReportError в предыдущем примере.
Обычно он вызывает диалоговое окно с сообщением. Каждый отдельный
видимый элемент отвечает за выдачу сообщения о любых ошибках,
поскольку программа не знает как проверять каждую из возможных
ситуаций.
Это важное достижение в технике программирования, поскольку
позволяет Вашей программе работать, как если бы все было правильно
вместо того, чтобы всегда смотреть что может быть неправильным.
Групповые объекты, включая программу, не беспокоятся о проверке
ошибок за исключением проверки, если какой-либо из их видимых
элементов был неверен. В этом случае группа просто удаляет себя и
свои подэлементы и указывает своему владельцу, что была
неправильной. Группа может предполагать, что ее неправильный
подэлемент уже сообщил пользователю о проблеме.
Использование Valid позволяет создавать окна и диалоговые
окна, рассматривая их как атомарные операции. Каждый подэлемент,
который создает окно, может быть создан без проверки на ошибку;
если констрактор неверен, он просто установит Valid в False. Если
любой подэлемент окна неверен, все окно возвращает False при
проверке. ValidView будет освобождать окно и возвращать nil. Все,
что требуется сделать - это проверить результат ValidView.
Основные потребители.
Функция Valid так же может обрабатывать "основных
потребителей", т.е. видимые элементы, которые распределяют память
больше, чем размер пула надежности, как например при чтении всего
файла в память. Основные потребители должны проверять LowMemory
сами вместо того, чтобы ожидать когда они закончат все создание, а
после этого позволят ValidView сделать это за них. Если основной
потребитель доходит до нехватки памяти в процессе своего создания,
он устанавливает свой флаг, который указывает, что встретилась
ошибка (как например флаг ErrorEncountered в предыдущем примере) и
больше не пытается распределить память. Этот флаг будет проверяться
в Valid и видимый элемент будет вызывать Application^.OutOfMemory и
возвращать False из вызова Valid.
Очевидно, что делать предположение о работе Ваших
констракторов - это не лучший вариант, но это единственный способ
управлять создание видимых элементов, которые превышают размер пула
надежности.
Программа FILEVIEW.PAS демонстрирует использование этой
техники для реализации надежного просмотра файла.
Назад | Содержание | Вперед