Книга: Технологии программирования
7.3. РЕТРОСПЕКТИВНОЕ ПРОЕКТИРОВАНИЕ ДЕМОНСТРАЦИОННОЙ ПРОГРАММЫ MCALC ФИРМЫ "BORLAND INC."
7.3. РЕТРОСПЕКТИВНОЕ ПРОЕКТИРОВАНИЕ ДЕМОНСТРАЦИОННОЙ ПРОГРАММЫ MCALC ФИРМЫ "BORLAND INC."
Согласно ретроспективно проведенного системного анализа (см. гл. 2), фирма "Borland Inc." приняла решение о реализации демонстрационного примера программы электронной таблицы. Вполне возможно сгенерировать множество вариантов реализации электронной таблицы, начиная от варианта со всеми клетками в одном окне и кончая, например, вариантом Excel. Однако фирма "Borland Inc." избрала вариант с прокруткой информации клеток в окне, изменением адресов клеток при вставках строк и столбцов, а также при их удалении. В проект введены требования разработки некоммерческого изделия. Размер таблицы ограничен 100–100 клетками. В программе отсутствует функция копирования клеток. Избранная сложность реализуемого варианта соответствует многофайловому проекту. Программа имеет функции поддержки вывода на дисплей, ввода с клавиатуры; в ней реализован интерпретатор формул с математическими функциями; для сохранения информации таблицы используется файл сложной организации, рассмотренный в гл. 3. Все это позволяет продемонстрировать возможности компилятора.
Программа Mcalc 1985–1988 гг. (Turbo Pascal 5.0) состоит из следующих файлов:
• mcalc.pas — файл основной программы;
• mcvars.pas — файл глобальных описаний;
• mcdisply.pas — файл подпрограмм работы с дисплеем;
• mcmvsmem.asm — ассемблерный файл подпрограмм запоминания в оперативной памяти информации экрана, а также восстановления ранее сохраненной информации экрана;
• mcinput.pas — файл подпрограмм ввода данных с клавиатуры;
• mcommand.pas — файл подпрограмм, обслуживающих систему меню и действий, выбранных посредством меню;
• mcutil.pas — файл вспомогательных подпрограмм;
• mcparser.pas — файл интерпретатора арифметических выражений формул клеток.
Все файлы закодированы с соблюдением развиваемых стандартов оформления. Так, в файлах mcdisply.pas, mcinput.pas описания прототипов подпрограмм выполнены с использованием более раннего синтаксиса языка программирования, что говорит об их заимствовании из программ, написанных ранее; при этом можно выявить их небольшое модифицирование (рефакторинг).
Хотя фирма "Borland Inc." занимается разработкой компиляторов, файл mcparser.pas также является заимствованным из UNIX YACC utility и лишь частично модифицированным. Остальные файлы являются оригинальными.
Ассемблерный файл mcmvsmem.asm является искусственно добавленным. Цель его добавления — демонстрация возможности использования ассемблерных вставок. Содержащиеся в нем алгоритмы вполне можно было бы реализовать на языке Pascal. Более того, можно было бы вообще обойтись без реализованных в нем подпрограмм, правда, при этом были бы видны некоторые задержки вывода информации на экран.
С целью совершения улучшающей проект новой проектной итерации получим из существующего проекта проектную документацию, состоящую из описания структуры данных программы; функционального описания основного ядра программы; схемы иерархии модулей основного ядра программы; спецификации назначения модулей основного ядра программы.
Рассмотрим организацию файла mcvars.pas, содержащего в основном описание структуры внутренних данных программы. Файл содержит описания в секции interface. Секция implementation пустая.
В начале файла содержится код, который в зависимости от наличия сопроцессора транслируется в одном из двух вариантов:
{$IFOPT N+}
{Есть встроенный сопроцессор}
type
Real = Extended; {Замена типа Real на Extended}
const
EXPLIMIT = 11356; {Предельное значение аргумента экспоненты}
SQRLIMIT = 1Е2466;{Предельное значение аргумента SQRT}
….
{$ELSE}
const
{Тип Real не переопределен}
EXPLIMIT = 88; {Предельное значение аргумента экспоненты}
SQRLIMIT = 1E18; {Предельное значение аргумента SQRT}
….
{$ENDIF}
Описания констант содержат следующие блоки:
— блок строчных констант, содержащих информацию всех выводимых на экран и в файлы текстовых надписей (для русификации всей программы требуется изменить только эту информацию);
— блок парных строк текстов меню и "горячих" клавиш выбора тем меню;
— блок описания важнейших констант, определяющих размерность таблицы и расположение информации на экране
MAXCOLS = 100; { Maximum is 702 } {Размер таблицы}
MAXROWS = 100;
MINCOLWIDTH = 3; {Минимальная ширина столбца}
MAXCOLWIDTH = 77; {Максимальная ширина столбца}
….
— блок описания цветов всех полей экрана, модификация констант которого позволяет оперативно изменять цвета;
— основные константы, мнемоника имен которых облегчает восприятие текстов программы
HIGHLIGHT = True; {Подсвеченная текущая клетка}
NOHIGHLIGHT = False; {Неподсвеченная клетка}
{Атрибут содержимого клетки}
ТХТ = 0;
VALUE = 1;
FORMULA = 2;
….
{Разрешенные буквы}
LETTERS: set of Char = ['A'..'Z', 'a'..'z'];
— коды управляющих клавиш клавиатуры.
Следует отметить, что приведены даже коды неиспользуемых в программе управляющих клавиш клавиатуры. Это соответствует факту копирования данных кодов из кода какой-то другой разработки.
Далее следуют описания типа информации содержимого табличной клетки и типа указателя на клетку:
type
CellRec = record
Error: Boolean;
case Attrib: Byte of
TXT: (T: IString);
VALUE: (Value: Real);
FORMULA: (Fvalue: Real;
Formula: IString);
end;
CellPtr = ^CellRec; {Указатель на клетку}
Данный тип организован так, что клетка всегда может содержать признак ошибки расчетов Error и размещать три варианта информации: текст, значение и формулу.
Далее описаны основные глобальные переменные. Описания начинаются с определения двухмерного, постоянно находящегося в памяти массива Cell указателя на клетки таблицы. Это позволяет не расходовать память на пустые клетки. Память под информацию клетки выделяется динамически в количестве, строго соответствующем информации клетки. Без использования динамически выделяемой памяти было бы невозможно разместить информацию клеток таблицы в 640К памяти машин того времени.
MAXCOLS*MAXROWS*[SizeOf(Istring)+SizeOf(Extened)] = 100*100*[80+10] = 900K
Далее следуют описание переменной, являющейся указателем на текущую клетку таблицы, описание массива форматов клеток и переменных позиционирования информации на экране.
Cell: array [1..MAXCOLS, 1..MAXROWS] of CellPtr;
CurCell: CellPtr; {Указатель на текущую клетку}
Format: array [1..MAXCOLS, 1..MAXROWS] of Byte;
LeftCol, RightCol, TopRow, BottomRow,
{Позиционирование}
CurCol, CurRow, LastCol, LastRow: Word;
….
Следует отметить, что выделение отдельного массива форматов информации клеток является не оправданным. Практичнее ввести байт информации формата клетки в тип CellRec.
Сравните этот проект структуры данных с проектом структуры данных электронной таблицы, представленной в гл. 3.
Для составления оставшейся проектной документации выполним трассировку программы. После двойного нажатия клавиши <F7> начинает исполняться настроечный код, содержащийся в файлах *.TPU, и далее начинают выполняться операторы основной программы program Mcalc, находящейся в файле mcalc.pas.
В результате исследований была выявлена схема иерархии модулей программы, изображенная на рис. 7.3–7.5. Расшифровка обозначений схемы иерархии представлена в табл. 7.1.
Рис. 7.3. Фрагмент схемы иерархии основных модулей программы
Рис. 7.4. Схема иерархии модуля RedrawScreen
Рис. 7.5. Сокращенная схема иерархии модуля Run
Таблица 7.1
Расшифровка обозначений схемы иерархии
Имя модуля | Файл | Назначение модуля |
Act | Mclib | Обрабатывает информацию введенной строки, занося ее в клетку |
CenterColString | Mcutil | Рассчитывает X координату центрируемой в поле вывода строки |
ChangeAutoCalc | Mclib | Устанавливает автоматический и ручной режимы рекалькуляции таблицы |
ChangeFormDisplay | Mclib | Устанавливает режим видимости значений формул или текста формул |
ClearInput | Mcdisplay | Очищает на экране поле строки ввода |
ClrScr | Crt | Очищает информацию в окне экрана |
DisplayCell | Mclib | Выводит на экран информацию клетки |
DisplayScreen | Mclib | Отображает на экране внутреннюю информацию таблицы |
EditCell | Mcommand | Осуществляет редактирование содержимого клетки |
EditString | Mcinput | Редактор текстовой строки |
EgaInstalled | Mcdisplay | Функция, определяющая наличие видеокарты EGA |
FillChar | Dos | Присваивает элементам массива значение символа |
GetCursor | Mcdisplay | Считывает толщину курсора в переменную |
GetInput | Mcinput | Получив первый введенный символ, продолжает ввод информации клетки |
GetKey | Mcinput | Формирует слово расширенного кода клавиши |
GetSetCursor | Mcdisplay | Считывает толщину курсора в переменную и устанавливает новую толщину курсора |
GotoXY | Mcdisplay | Перемещает курсор в соответствии с заданными координатами дисплея |
InitColorTable | Mcdisplay | Инициализирует массив пересчета цветов для монохромного монитора |
InitDisplay | Mcdisplay | Инициализирует видеокарту на работу в режиме 80-25 |
InitVars | Mcutil | Инициализирует значения основных переменных программы |
Intr | Dos | Вызывает прерывание MS DOS |
LoadSheet | Mcommand | Загружает информацию таблицы из файла |
MainMenu | Mcommand | Реализует выбор тем меню программы |
Mcalc | Mcalc | Главная программа |
ParamCount | Dos | Счетчик полей командной строки запуска программы Mcalc |
ParamStr | Dos | Возвращает значения заданного поля командной строки запуска программы Mcalc |
PrintCol | Mcdisplay | Выводит значение координаты колонки таблицы |
PrintFreeMem | Mcdisplay | Выводит на экран значение остатка свободной памяти |
PrintRow | Mcdisplay | Выводит значение координаты строки таблицы |
ReadKey | Mcinput | Считывает короткий код одной нажатой клавиши |
Recalc | Mclib | Осуществляет перерасчет значений формул клеток таблицы |
RedrawScreen | Mclib | Отображает на экране всю информацию таблицы |
Run | Mcalc | Главный цикл программы |
Scroll | Mcdisplay | Прокручивает информацию экрана в указанном направлении; устанавливает цвет фона освободившейся части экрана |
SetBottomRow | Mcdisplay | Выводит на экран столбец с номерами строк таблицы |
SetColor | Mcdisplay | Устанавливает цвет вывода строк на экран |
SetCursor | Mcdisplay | Устанавливает заданную толщину курсора |
SetRightCol | Mcdisplay | Выводит на экран строку с наименованиями столбцов таблицы |
ShowCellType | Mcdisplay | Выводит на экран надпись о типе текущей клетки таблицы |
TextMode | Dos | Переводит экран в указанный текстовый режим |
Window | Crt | Определяет окно на экране дисплея |
Write | - | Оператор вывода языка Pascal |
WriteXY | Mcdisplay | Осуществляет вывод заданного числа символов заданной строки по заданным координатам дисплея |
Рассмотрим функциональное описание основного ядра программы. В файле mcutil.pas исполняется рудиментарный, оставшийся от прежних разработок код:
HeapError:= @HeapFunc;
В файле mcdisplay.pas последовательно выполняются подпрограммы: InitDisplay, GetSetCursor, Window, EGAInsalled.
Процедура InitDisplay инициализирует видеокарту на работу в режиме 80 25 при помощи вызова прерывания 10h и вызовом процедуры InitColorTable инициализирует массив пересчета цветов для монохромного монитора. Последний массив используется при вызовах процедуры SetColor.
Процедура GetSetCursor при помощи процедуры GetCursor считывает толщину курсора в переменную OldCursor и при помощи процедуры SetCursor устанавливает новую толщину курсора (NOCURSOR).
Процедура Window определяет окно на экране дисплея для размещения информации всей таблицы. Далее начинает выполняться код главной программы Mcalc.
Присваиванием CheckBreak:= False запрещается использование клавиши <Ctrl+Break> немедленного завершения программы.
Вывод начальной заставки осуществляется следующими вызовами подпрограмм. Процедурами SetColor и ClrScr производится очистка окна программы. Двойным вызовом процедур SetColor и WriteXY выводятся две строки начальной заставки. Несмотря на отсутствие курсора, отрабатывается рудиментарный вызов "сокрытия" курсора GotoXY(80,25). При помощи функции GetKey осуществляется ожидание нажатия пользователем любой клавиши.
Процедурами SetColor и ClrScr производится очистка окна программы.
Вызовом процедуры InitVars инициализируются значения основных переменных программы. Массивы инициализируются значениями по умолчанию вызова процедуры FillChar.
Присваиванием Changed:= False указывается факт неизменности информации клеток таблицы после момента инициализации переменных для запрещения срабатывания автосохранения.
Вызовом процедуры RedrawScreen производится отображение на экране всей информации таблицы.
Если значение ParamCount = 1, то в командной строке MS DOS вызова программы было указано имя файла таблицы. В этом случае выполняется процедура LoadSheet, которая загружает информацию таблицы из файла с именем файла, полученном при помощи вызова функции ParamStr.
Наконец, отрабатывает "лишний" вызов Clearlnput, который дублируется в начале последующей процедуры Run, содержащей главный цикл программы.
При завершении выполнения программы последовательно производится установка цвета экрана, вызовом TextMode переводится экран в текстовый режим, запомненный в переменной OldMode, и, наконец, вызовом SetCursor восстанавливается толщина курсора, запомненная в переменной OldCursor.
Работа процедуры RedrawScreen заключается в последовательном выводе на экран информации:
• процедурой SetRightCol выводится на экран строка с наименованиями столбцов таблицы;
• процедурой SetBottomRow выводится на экран колонка с номерами строк таблицы;
• процедурами GotoXY и Write выводятся надписи в верхней строке экрана, хотя имеется более удобная процедура WriteXY;
• выводится число остатка байт памяти;
• процедурой DisplayScreen отображается на экране внутренняя информация таблицы.
Внешний вид программы Mcalc приведен на рис. 7.6.
Работа процедуры Run начинается с установления переменной главного цикла Stop:= False и выполнения процедуры Clearlnput. Главный цикл программы выполняется до изменения значения переменной Stop на True. Такое изменение возможно лишь при выборе пользователем темы меню Quit — завершение работы с программой.
Внутри главного цикла последовательно выполняются следующие действия:
— при помощи процедуры DisplayCell выводится на экран подсвеченная клеточным курсором текущая клетка (клетка А1 на рис. 7.6);
— при помощи процедуры ShowCellType выводится в нижнем левом углу экрана надпись типа текущей клетки таблицы (см. рис. 7.6);
— оператором Input:= GetKey в переменную Input вводится код символа клавиши, нажатой пользователем;
— выполняются действия отработки клавиши, нажатой пользователем.
Действия отработки клавиши, нажатой пользователем, представляют собой цепочку альтернативных действий, реализованную структурой ВЫБОР. Сначала отрабатываются действия "горячих" клавиш. В секции default (если клавиша не была "горячей") вызовом процедуры Getlnput начинается занесение информации в текущую клетку таблицы. Процедура Getlnput, занеся символ Input в редактируемую строку, первоначально вызывает EditString — редактор текстовой строки информации клетки и затем вызывает процедуру Act, которая обрабатывает информацию введенной строки, занося ее в клетку.
Рис. 7.6. Внешний вид программы Mcalc
Анализ схемы иерархии программы и функционального описания основного ядра программы показал, что основная программа перегружена вспомогательными действиями, выделение процедуры Run является искусственным разделением основной программы без продуманного структурного разбиения. Все это приводит к потере понятности текста программы.
С целью повышения понятности программы были приняты новые проектные решения, отраженные схемой иерархии (рис. 7.7).
Выполнение основной программы Mcalc начинается с запуска нового модуля Starting подготовительных действий программы. Модуль Starting является монитором последовательного исполнения модулей InitDisplay, Greeter, InitVars.
Новый модуль InitDisplay теперь является монитором последовательного исполнения модулей GetSetMode, GetCursor, SetCursor, Egalnstalled, Window, InitColorTable.
У нового модуля GetSetMode явно в качестве входного параметра указывается новый устанавливаемый видеорежим, а на выходе — старый видеорежим. Такая организация предпочтительнее прямого вызова Intr, поскольку по списку формальных параметров ясно видно назначение модуля. Реализация двух функций по выявлению и установке видеорежимов в одном модуле здесь вполне оправдана, поскольку все они реализуются вызовом одного прерывания.
Не является оправданным использование модуля с двумя функциями GetSetCursor, который являлся монитором последовательного исполнения модулей GetCursor, SetCursor. Этот модуль исключен из проекта. Все функции вывода начальной заставки переданы новому модулю Greeter.
Из модуля RedrawScreen исключен вызов модуля DisplayScreen. Это позволило избежать повторного вызова модуля DisplayScreen в модуле LoadSheet. Также исправлена ошибка использования операторов Write для вывода информации на экран путем использования вызовов процедуры WriteXY.
Далее начинает исполняться главный цикл программы. Модуль Run удален из проекта с целью увеличения понятности программы. Длинный текст выбора действий по коду нажатой пользователем клавиши заменен одной альтернативой:
If (not(HotKey(Input)) and (ConditionalKey(Input)))
then
GetInput(Input).
Новая функция HotKey в случае нажатия пользователем горячей клавиши возвращает значение TRUE, в противном случае функция возвращает значение FALSE.
Новая функция ConditionalKey в случае нажатия пользователем клавиши с кондиционным для занесения в таблицу кодом возвращает значение TRUE, в противном случае функция возвращает значение FALSE.
Рис. 7.7. Переработанная схема иерархии модулей программы
Новая процедура WriteXY теперь не использует вызов медленной процедуры GotoXY и медленно выполняемый оператор Write и использует прямой доступ к видеопамяти. Это позволило значительно ускорить вывод информации на дисплей. Более того, в процедуру добавлен новый параметр атрибута цвета выводимой строки, что позволило избежать цепочек первоначального вызова SetColor, а затем WriteXY.
Завершается выполнение программы вызовом нового модуля Finishing. Данный пример показал самодостаточность избранной проектной документации для получения нового оптимального варианта построения структуры программы.
ВЫВОДЫ
• Структура программы — искусственно выделенные программистом взаимодействующие части программы. Использование рациональной структуры устраняет проблему сложности разработки; делает программу понятной людям; повышает надежность работы программы при сокращении срока ее тестирования и сроков разработки вообще.
• Модуль — функциональный элемент технологии структурного программирования. Это подпрограмма, но оформленная в соответствии с особыми правилами.
• В понятие структуры программы включается состав и описание связей всех модулей, которые реализуют самостоятельные функции программы и описание носителей данных, участвующих в обмене как между отдельными подпрограммами, так и вводимыми и выводимыми с/на внешних устройств.
• Вероятно, наиболее общая тактика программирования состоит в разложении процесса на отдельные действия: функционального описания на подфункции, а соответствующих программ — на отдельные инструкции.
• Самым главным в схеме иерархии является минимизация усилий по сборке и тестированию программы. При использовании заглушек можно хорошо тестировать сопряжения модулей, но не сами модули. Тестирование самих модулей потребует изощренных сложных заглушек и астрономического числа тестов. Выход — до интеграции модулей тестировать модули с использованием ведущих программ.
• Схема иерархии должна отражаться на файлах с исходными текстами программ таким образом, чтобы каждый файл содержал как можно больше готовых функций с общим назначением. Это облегчит их использование в последующих разработках.
Контрольные вопросы
1. Дайте определение понятию "структура программы".
2. Что такое модуль программы и какими характеристиками он должен обладать?
3. Что отражает схема иерархии?
4. Какие принципы необходимо соблюдать, если следовать технологии структурного программирования?
5. Дайте определение понятию "заглушка модуля".
6. Перечислите основные средства изменения топологии схемы иерархии программы.
7. Назовите критерии оценки качества схемы иерархии.
8. Для чего нужен паспорт модуля?
- Восстановление "безнадежных" баз данных. InterBase Surgeon
- СТРУКТУРА ПРОСТОЙ ПРОГРАММЫ
- Основные "рычаги" управления производительностью
- ПРИМЕР ПРОСТОЙ ПРОГРАММЫ НА ЯЗЫКЕ СИ
- Разработка приложений баз данных InterBase на Borland Delphi
- 1.2.5. Пример программы
- Using Double Quotes to Resolve Variables in Strings with Embedded Spaces
- 24.7. Использование программы-твикера
- Ttl-inc.txt
- Метод Distinct
- Часть III Прикладные программы
- Цикл создания программы