Глава 2 Стратегия разработки приложений
Файлы, связанные отношением один-ко-многим Большинство начинающих и среднего уровня FoxPro-программистов способны создавать экраны для работы с плоскими файлами.
Однако, реальные приложения не ограничиваются работой с плоскими файлами. Если вы посмотрите на экран программы ввода данных по счетам, вы увидите в верхней части экрана информацию о, скажем, заказчике, и итоговые цифры внизу. В середине вы найдете строки с данными по позициям, включенным в счет. Эти данные мы получаем из другого файла, который иногда называют файлом детальной информации или дочерним файлом. Различные способы организации связи между двумя группами записей - составляющих заголовок наверху экрана и данными в средней части - одна из наиболее интересных тем программирования на FoxPro.
Экраны на базе BROWSE для работы с файлами,
связанными отношением один-ко-многим
Fox Software рекомендует использовать BROWSE для организации вывода записей из связанных файлов. Команда BROWSE NOWAIT оставляет окно BROWSE активным, так что при перемещении указателя записи в родительском файле, указатель в дочернем файле также перемещается. В Листинге 2-1 показан пример программы.
Такой подход дает вам окно с записями из дочернего файла, указатель в котором следует за указателем в родительском файле. Меню можно активизировать в любое время, нажав клавишу F10. Вы можете воспользоваться мышью для перехода в окно BROWSE и просмотра тех записей, которые не умещаются в окне. Предложение ACTIVATE команды READ обеспечивает автоматический возврат в рабочую область с родительским файлом при завершении работы с BROWSE.
Этот метод обеспечивает нам минимальную функциональность; пример с вводом данных по оплате, приведенный в следующей главе, более полно раскрывает разнообразные возможности команды.
Как самому написать BROWSE
Почему только все пишут свои варианты BROWSE? Fox Software включила в команду все, что только можно вообразить; структуры VALID и WHEN могут быть использованы как на уровне команды в целом, так и на уровне отдельных полей; поля могут быть привязаны к пользовательским функциям, способным отыскивать информацию в других файлах; клавиши-ускорители могут быть использованы для управления программой. В чем же дело?
Именно богатство возможностей и служит причиной возникновения проблем. Если вы войдете в Compuserve (глобальная информационная сеть, - прим. пер.), большая часть сообщений в FoxForum (раздел Compuserve, служащий для общения пользователей и разработчиков Fox, - прим. пер.) посвящена соображениям относительно скорости работы или точнее - отсутствия таковой. Rushmore действительно обеспечивает высокую производительность, но мои пользователи большую часть времени заняты вводом данных. Не помню когда последний раз мне доводилось видеть как курсор перемещается между полями ввода. Теперь я это могу видеть.
В Fox Software вам скажут, что пользователи должны работать на 386 машинах, оснащенных расширенной памятью. Я работаю с пользователями, использующими десятки AT и не собирающимися расставаться с ними, пока компьютеры работают, и они найдут другого программиста, если я не смогу выполнить задание. Им нет дела до того, что мне очень нравится работать с FoxPro.
При работе с FoxPro 2.0 вы способны создать больше программ за меньшее время, с меньшими затратами чем это возможно в любой другой среде разработчика. Тем не менее, если заказчик считает, что программа работает невозможно медленно, я должен с этим считаться.
К счастью, есть способы заставить FoxPro работать быстрее. Сведя к минимуму обращение к различным файлам и избегая наиболее медленных команд, вы можете получить вполне приемлимую скорость. До сих пор единственным исключением была BROWSE.
BROWSE называют языком внутри языка. Команда в последнем варианте имеет 28 параметров, и я подозреваю, что с течением времени будут добавлены другие. Здесь-то и зарыта собака.
Когда вы вызываете BROWSE, FoxPro считывает код из оверлейного файла и посылает его вам по кабелю на рабочую станцию. Это то замедление, которое вы имеете перед появлением окна. Настоящее же замедление проявляется когда вы перемещаете указатель в базе данных, так как BROWSE должна обрабатывать все ситуации, определяемые названными 28 параметрами. Это большой кусок кода. (Некоторое ускорение работы можно получить, если вызывать BROWSE с параметрами NO...: NOEDIT, NOMENU, NOAPPEND и др., - прим. пер.)
Проблемы скорости, не видимые пользователям
Есть еще одна проблема, которая не беспокоит моих пользователей непосредственно, но стоит им денег. Если вы включили в программу сложную команду BROWSE, вам, возможно, приходилось переходить в пошаговый режим для отладки. Есть у вас минутка? Если в диапазон, охватываемый BROWSE, попадает 4 записи, команда BROWSE в окне трассировки мигнет 4 раза. Если у вас 600 записей, она мигнет 600 раз. После чего вы можете продолжить отладку, конечно если вы к этому моменту еще не заснули. На 286 рабочих станциях на сети ситуация еще хуже.
Недавно мне пришлось иметь дело с медицинским колледжем, в распоряжении которого была комната, полная 8088 машин. Не знаю куда идут все те деньги, что мы выплачиваем в качестве налогов, но совершенно очевидно, не на оснащение компьютерами медицинских школ. Легко сказать, что руководство школ должно понимать какие потери времени это влечет, но факт в том, что старые компьютеры по-прежнему существуют.
Третья проблема BROWSE не касается скорости. Я получаю заказы от пользователей, желающих, чтобы экраны работали определенным образом. Некоторые хотят видеть аналог электронной таблицы, другим нужны формы типа бланков. BROWSE не всегда можно заставить работать так, как вам того хочется.
Например, мне доводилось видеть весьма замысловатые интерфейсы, где назначение некоторых клавиш, скажем Del и Ins, менялось в зависимости от контекста. Если вы находитесь в режиме просмотра, Del удаляет запись, при редактировании происходит удаление символа. Такие вещи не просто организовать в BROWSE.
Получается, некоторые поля ввода данных должны работать определенным образом, что не всегда возможно реализовать в BROWSE. Даже если скорость исполнения и отладки не имеет значения, мне платят достаточно хорошо за то, что я делаю. В этом заключается стимул. Задача состоит в создании достаточно гибкой, легкой в работе и быстрой программы.
После анализа нескольких программ, обеспечивающих прокручивание строк данных и управляемых функцией INKEY(), я попытался представить BROWSE как прокручивающееся окно с участком редактирования в виде окна без рамки, высотой в одну строку. Если прокручивание выполняется процедурой, занятой только этим, она должна работать достаточно быстро. После чего, создав механизм перехода от просмотра к редактированию, я мог бы вызвать команду READ, связанную со структурами VALID и WHEN, как это сделано в BROWSE.
Предложите мне решение, а не проблему
Первый вариант программы включал использование INKEY() в цикле и работал довольно быстро. Я вывел на экран группу записей и применил технику отслеживания текущей записи - при перемещении курсора перемещается указатель в базе данных.
При нажатии Enter я подключал команду READ CYCLE для редактирования текущей строки, выводимой из массива, по окончании редактирования я попадал в режим просмотра.
Все выглядело замечательно, пока я не нажал F10 для активизации системного меню. Система не реагировала. Оказывается, вы не можете добраться до системного меню кроме как из состояния ожидания, создаваемого READ. Цикл с INKEY() - это конечно не READ.
Ну ладно, если не учитывать все мелочи, как вам это нравится? Похоже, что мне удалось создать быстро работающий, управляемый вариант BROWSE, который может быть использован вместе с системным меню.
Осталось только определить момент выхода из редактирования. Как мне обработать нажатия клавиш, в других случаях перехватываемых INKEY()? Но ведь есть же предложение READ VALID. Все, что мне нужно - это проверка на готовность выхода, а ON KEY LABEL обработает все остальное.
После часа экспериментирования стало ясно, что я на верном пути, а через пару дней мне удалось добиться нужной функциональности. Код приведен в Листинге 2-2. Он позволяет настроить его как вам заблагорассудится.
Я был поражен насколько хорошо это все работало. Я мог вызвать меню _MSYSMENU, и оно работало как я ожидал. Для вызова режима редактирования я использовал Сtrl-E, так как нельзя было переназначать Enter, потому что кто-нибудь мог вызвать системное меню и попытаться выбрать опцию. Тем не менее, результат был интуитивно очень понятным. Кроме того, я не привлекаю BROWSE и, соответственно, не получаю задержек, связанных с загрузкой сегментов, когда вы работаете на 286 рабочей станции программа стартует мгновенно. Мои тесты показывают прирост скорости от 300 до 500% по сравнению с BROWSE FoxPro. Увеличение производительности особенно заметно на 8088 или 80286 машинах, но ощущается даже на 80486.
Как это работает
Рассмотренная программа служит заменой стандартному окну BROWSE. Она использует окно с прокруткой, имитирующее BROWSE. Когда пользователь, нажав клавиши Ctrl-E, выбирает режим редактирования текущей записи под курсором, текущая строка становится областью редактирования при работе команды READ второго уровня.
Мне страшно не нравилась необходимость нажимать Ctrl-E для входа в режим редактирования. В конце концов программа призвана заменить BROWSE, которое всегда находится в режиме радактирования. Почему бы не попробовать обеспечить 100% совместимость?
Это один из наиболее важных аспектов программирования. В какой момент вам нужно отложить в сторону свои первоначальные задумки и начать делать нечто иное? Задача заключается в получении максимума удовольствия за минимальную цену. К сожалению, альтернативные решения становятся очевидными только после того, как вы попробовали их все. Именно это я и сделал в нашем случае. Во всяком случае, если прямой переход к редактированию важен, вы можете использовать KEYBOARD [{Ctrl+E}] для перехода к следующей строке.
Когда пользователь не находится в режиме редактирования, назначения клавиш определяют, что происходит при том или ином действии. При начале редактирования команда PUSH KEY CLEAR снимает все назначения с клавиш до момента, когда редактирование окончено. Для отключения всех назначений функциональных клавиш я использую функцию ClearFKeys().
Вы можете заметить, что все назначения, связываемые с "горячими клавишами" командами ON KEY LABEL, начинаются с команды PUSH KEY CLEAR и восстанавливаются командой POP KEY перед завершением функции. Назначения клавиш сохраняются при работе вызванных процедур, и если пользователь продолжает нажимать на клавишу во время выполнения процедуры, вы получаете нежелательные рекурсивные вызовы. Может показаться, что назначения снимаются всего лишь на секунду, но для компьютера это несколько сотен тысяч машинных циклов.
Функции, отмеченные как "стандартизованные", являются общими для всей программы. Такие вещи как рисование сетки на экране выполняются достаточно просто, если вы используете GetCols() для определения начального столбца. В программе есть флаг WireFrame, определяющий будет или нет присутствовать сетка.
Возможно, вам придется модифицировать программу. На первый взгляд кажется, что это немалый труд, на самом деле большая часть кода аналогична тому, что вы использовали бы при работе с BROWSE. Несколько специализированных функций можно разделить на три категории:
1) Шесть функций позиционирования - они используют назначения клавиш, соответствующие вашим представлениям о работе программы, и вам, может быть, необходимо указать, какие записи на текущей странице являются первой и последней, используя ваши массивы и иные отметки положения.
2) Процедуры вывода текущей записи и поля GET для режима редактирования - они используют те же самые положения столбцов, что были рассчитаны функцией GetCols(). Однако структуры VALID относятся только к GET.
3) Код манипулирования данными массива, инициализации и загрузки его элементов, сохранения данных после редактирования, удаления и вставки элементов.
Я включил в программу функцию DeleteLine() и процедуры, которые она вызывает, все модули стандартизованы. Кроме того в программу включены модули для открытия файлов, одновременно выполняющие задачу документирования структур используемых файлов. Если вы будете включать их в свою программу, то вызовы должны размещаться на более высоком уровне.
Функция UnCDX() служит для решения маленькой проблемы, обычно решаемой программой обработки ошибок. Байт 29 заголовка .DBF- файла содержит 1h, если файл сопровождается структурным индексом. Если индексный файл не найден, вы получаете сообщение об ошибке. Ошибка с кодом 1707 может быть перехвачена, но попробуйте и мой подход, он работает очень быстро.
Самое интересное я оставил напоследок. Если вам когда-либо доводилось видеть Quicken вы, возможно, восхищались многострочным BROWSE, используемым как основа для разработки форм ввода. Так вот, программа, которую вы только что разобрали, имеет примерно с десяток отличий, но выполняет ту же самую функцию. Текст программы включен в дискету, которую вы можете заказать отдельно.
Использование списков на базе массивов
для управления дочерними записями
При работе с относительно небольшим количеством записей, скажем менее ста, могу предложить еще один способ. Такой подход заключается в создании массива, в который перекачивается содержимое записей. Команда @ GET... FROM ARRAY используется для вывода прокручивающегося списка и определения номера выбранного элемента. Выбор производится нажатием Enter. Программа в Листинге 2-3 предлагает пример подобной техники.
У приведенного подхода только один недостаток: так как вы не можете определить какая часть массива прокручена за пределы экрана, редактирование выбранной дочерней записи непосредственно на месте, как это принято в BROWSE, невозможно. Однако, использованный здесь подход с окном редактирования работает достаточно хорошо, чтобы компенсировать этот недостаток, кроме того, вы выигрываете в скорости и простоте. Кроме того, я включил несколько других полезных вещей:
1) Добавление нового клиента непосредственно по ходу работы.
2) Просмотр/печать записей с использованием средств слияния текста и программы Print.com из состава DOS.
Весь экран построен на базе полей GET. Если не выбран режим редактирования, активны только кнопки управления, определяющие переменную m.WhaToDo, тогда как поля ввода данных недоступны. Структура предложения VALID для переменной m.WhaToDo обеспечивает снятие блокировки с полей ввода и блокирует кнопки управления при выборе режима редактирования или добавления записи.
Для поиска записей я использовал функцию Jkey.plb, написанную Джо Готтхельфом, придающую BROWSE средства поиска с последовательным уточнением.
Приведенный подход вполне годится для большинства приложений. Он быстр, программа не требует значительных усилий и обеспечивает вам необходимый дополнительный контроль. Кроме того, у некоторых приложений ограничение числа дочерних записей является необходимым условием, что делает такую методику исключительно удобной.
Заключение
В этой главе я рассмотрел три способа включения средств управления дочерними записями в экран ввода. BROWSE - очень мощное и, совершенно очевидно, наиболее простое для программирования средство, но иногда не обеспечивает необходимой скорости. Иногда требуется добавить определеную функциональность, которую не так легко реализовать средствами BROWSE. Отладка BROWSE также может составить проблему. К счастью, FoxPro позволяет вам разрабатывать собственные инструменты, больше подходящие для решения конкретных задач.
В следующей главе мы рассмотрим BROWSE как основу для разработки сложного приложения.