
- •Введение
- •Структура модуля
- •Заголовок модуля
- •Подключение модулей
- •Раздел интерфейса
- •Раздел реализации
- •Раздел инициализации
- •Раздел деинициализации
- •Разработка приложений с использованием модулей
- •Организация файлов в каталоге программы
- •Оформление Windows программ
- •Стандартные модули
- •Модуль System
- •Модуль Math
- •Модуль SysUtils
- •Задания к лабораторной работе
- •Вопросы к лабораторной работе
- •Приложение А

Разработка приложений с использованием модулей
Существует два различных подхода при разработке программы использующей модули.
•При первом подходе модуль используется просто как библиотека подпрограмм, констант, типов и пр. Примером таких модулей могут служить рассмотренные ниже модули System, SysUtils или Math. Особенностью такого подхода является то, что модуль экспортирует практически все объявления.
•Второй подход предполагает использование модуля для сокрытия данных (инкапсуляции). Это постигается путем помещения данных в секцию реализации (implementation) и осуществление доступа к ним с помощью подпрограмм, которые, в свою очередь экспортируются (interface) модулем. Это предотвращает неправильное использование данных и гарантирует их целостность.
Рассмотрим небольшой пример, для иллюстрации второго подхода. Предположим, что имеется две переменные первая переменная (Count) имеет тип integer, а вторая (isEven) тип boolean. Причем на их значения наложены следующие ограничения:
•если число в Count четное, то isEven равно true, иначе False;
•переменная Count доступна как для чтения, так и для записи, а переменная isEven доступна только для записи.
Пример модуля, реализующий такой функционал, приведен ниже.
Листинг 1 – Исходный текст модуля uDemo.
{$IFDEF FPC} {$MODE DELPHI}
{$ENDIF} unit uDemo; interface
procedure SetCount(ACount:Integer); function GetCount:Integer; function GetisEven:Boolean;
implementation
var Count:Integer; isEven:Boolean;
procedure SetCount(ACount:Integer); begin
Count:=ACount; isEven:=Count mod 2 = 0;
end;
function GetCount:Integer; begin
Result:=Count;
end;
function GetisEven:Boolean; begin
Result:=isEven;
end;

initialization Count:=0;
isEven:=Count mod 2 = 0;
end.
Как видно, модуль очень прост и не нуждается в комментариях. Видно, что нет возможности записать в переменную Count четное число и установить значение переменной isEven равным False. Это означает, что переменные всегда будут находится в согласованном состоянии.
В первых строках модуля с помощью директивы {$MODE} активируется режим совместимости в диалектом Object Pascal, реализованном до Delphi 7 включительно. Учитывая, что в Delphi нет этой директивы, она исключается из исходного кода с помощью директив условной компиляции. Это позволяет компилировать этот модуль в
Free Pascal или Delphi без доработок.
Листинг 2 – Пример использования модуля uDemo
program IsEvenDemo; {$APPTYPE CONSOLE} {$IFDEF FPC}
{$MODE DELPHI} {$ENDIF}
uses uDemo; begin
SetCount(1);
Writeln(GetisEven);
SetCount(2);
Writeln(GetisEven);
Readln;
end.
Рассмотрим еще один пример, программу для работы со списком студентов из лабораторной работы «Процедуры и функции». Перепишем ее с использованием модулей.
Всю логику работы с таблицей поместим в модуль uStudList.
Листинг 3 – Исходный текст модуля uStudList
{$IFDEF FPC} {$MODE DELPHI}
{$ENDIF}
unit uStudList;
interface
//добавление записи в конец массива procedure AddRecord;
//печать таблицы procedure ShowAllRecord;
//инициализация procedure InitStudList;
implementation
const n=10; //емкость массива
type TStudent=record |
|
|
name |
:string[12]; //имя |
|
familia:string[16]; //фамилия |
||
gruppa |
:Integer; |
//группа |

end;
var Table:array [1..n] of TStudent; //таблица Count:integer=0; //число строк в таблице
//ввод записи
procedure InRecord(var P:TStudent);
begin |
|
writeln('Insert |
record >>'); |
with P do begin |
|
write('name |
:'); readln(name); |
write('familia |
:'); readln(familia); |
write('gruppa |
:'); readln(gruppa); |
end;{with} |
|
end;{InRecord} |
|
//очистка списка procedure InitStudList; begin
Count:=0; end;{InitStudList}
//вывод записи
procedure ShowRecord(const P:TStudent); begin
with P do writeln(name, ' ', familia, ' ', gruppa); end;{ShowRecord}
//добавление записи в конец массива procedure AddRecord;
begin
if Count=n then //если число элементов превышает емкость массива writeln('Error: array overflow')
else begin//если в массиве есть место, то добавляем запись inc(Count);
InRecord(Table[Count]); end;{else}
end;{AddRecord}
//печать таблицы |
|
procedure ShowAllRecord; |
|
var i:integer; |
|
begin |
|
//рисуем таблицу |
|
writeln('|-------------------------------------------- |
|'); |
for i:=1 to Count do begin |
|
write(i, ') '); |
|
ShowRecord(Table[i]); |
|
end; |
|
writeln('|-------------------------------------------- |
|') |
end; |
|
end. |
|
Видно, что данные таблицы скрыты от непосредственного доступа. Обработка данных может осуществляться только посредством трех экспортированных подпрограмм секции interface.
Программа демонстрирующая работу с модулем uStudList приведена в листинге 4.
Листинг 4 – Пример программы использующей модуль uStudList
program TableRec; {$APPTYPE CONSOLE}

{$IFDEF FPC} |
|
|
{$MODE DELPHI} |
|
|
{$ENDIF} |
|
|
uses uStudList; |
|
|
|
//доступные команды |
|
const |
cmdExitProgram=0; |
//выход из программы |
|
cmdAdd=1; |
//добавление записи |
|
cmdShowAll=2; |
//показать все записи |
//выводит меню, возвращает результат выбора function ShowMenu:Integer;
begin writeln;
writeln('Viberite deistvie'); writeln(cmdExitProgram, ' - Exit program'); writeln(cmdAdd, ' - Add record'); writeln(cmdShowAll, ' - Show all record'); write('>>'); readln(Result);
end;
var MenuState:byte;//код выбранного действия
begin
InitStudList; //очистка списка комманд
repeat //выводим меню
MenuState:=ShowMenu;
//выбираем действие case MenuState of cmdAdd: AddRecord;
cmdShowAll: ShowAllRecord; cmdExitProgram: begin
writeln('Press Enter to exit'); readln;
end;{cmdExitProgram} else
Writeln('Unknown command'); end;{case}
until MenuState=cmdExitProgram;
end.
Эту программу еще можно улучшить, с точки зрения модульности. Для этого перенесем код, реализующий меню в отдельный модуль.
Листинг 5 – Исходный текст модуля uCommandList
{$IFDEF FPC} {$MODE DELPHI}
{$ENDIF}
unit uCommandList; interface
const errOverflowCmdsList=-1; type THandlerProc=procedure;
//выполнять список команд, пока не вызвана команда с кодом exitcode
procedure Run(exitcode:Integer);
{добавить комманду

ВХОД
ACommandName - имя команды
AHandler: - процедура, обработчик команды ВЫХОД внутренний код добавленной команды
}
function AddCommand(const ACommandName:string; AHandler:THandlerProc):Integer;
//сброс списка комманд procedure InitCommands;
implementation
const max_command_count=16;
type THandlerRec=record fcommand_name:string; //имя команды fcommand_id:Integer; //код команды
fhandler:THandlerProc; //обработчик команды end;
var command_list:array[0..max_command_count-1] of THandlerRec; command_count:Integer;
procedure InitCommands; var i:Integer;
begin
//очистка массива
for i:=Low(command_list) to High(command_list) do with command_list[i] do begin
fcommand_name:=''; fcommand_id:=0; fhandler:=nil; end; command_count:=0;
end; |
|
function AddCommand( |
const AcommandName:string; |
|
Ahandler:THandlerProc |
):Integer; begin
if command_count<>max_command_count then with command_list[command_count] do begin fcommand_name:=ACommandName; fHandler:=AHandler; fcommand_id:=command_count; Result:=command_count; Inc(command_count);
end
else Result:=errOverflowCmdsList; end;
procedure Run(exitcode:Integer); var i, command, findcom:Integer; begin
if command_count<>0 then repeat
//выводим меню writeln;
writeln('Viberite deistvie');

for i:=0 to command_count-1 do begin
Writeln(i, ')', command_list[i].fcommand_name); end;
//ввод кода команды write('>>'); Readln(command);
//просмотр списка команд i:=0; findcom:=-1;
while True do begin
if i<command_count then
if command_list[i].fcommand_id=command then begin findcom:=i;
Break; //выход элемент найден end
else
else Break; //выход, список закончилcя
Inc(i); end;{while}
if findcom=-1 then Writeln('Unknown command') else
if Assigned(command_list[findcom].fhandler) then command_list[findcom].fhandler;
until command=exitcode; end;{Run}
end.
Тест модуля предельно прост, поэтому прокомментируем только одну новую функцию
function Assigned(const P): Boolean;
Эта функция возвращает True, если переменной ссылочного типа (процедурная переменная, указатель, динамический массив, объект, интерфейс, ссылка на метод) было присвоено какоелибо значение, иначе возвращается False. В нашем модуле она используется для проверки того, что команде назначен обработчик, т. к. обращение к процедурной переменной, которой не присвоено значение вызовет крах программы.
Модуль uCommandList реализует работу со списком команд в общем виде и может применяться в различных проектах, как отдельный и независимый компонент, что увеличивает коэффициент повторно используемого кода.
Пример программы использующей оба модуля приведен ниже.
Листинг 6 – Модифицированная версия программы TableRec
program TableRec; {$APPTYPE CONSOLE} {$IFDEF FPC}
{$MODE DELPHI} {$ENDIF}
uses uStudList, uCommandList;
procedure ExitAction; begin
writeln('Press Enter to exit'); readln;
end;
function NewCommand(const ACommandName:string; AHandler:THandlerProc):Integer;
begin

Result:=AddCommand(ACommandName, AHandler); if Result=errOverflowCmdsList then begin Writeln('Overflow command list'); Halt(1);
end;
end;
var exitcode:Integer; begin
InitStudList; //очистка списка студентов InitCommands; //очистка списка команд
exitcode:=NewCommand('ExitCommand', @ExitAction); NewCommand('Addrecord', AddRecord); NewCommand('ShowAllRecord', ShowAllRecord);
Run(exitcode);
end.
Для иллюстрации другого подхода к разработке модулей поместим процедуры для работы с массивами из лабораторной работы «Процедуры и функции» в отдельный модуль uIntArrayUtils. Текст модуля приведен в Приложении А. В данном случае, модуль выступает только в качестве библиотеки подпрограмм. Пример использования этого модуля приведен в листинге 7.
Листинг 7 – Демонстрация работы модуля uIntArrayUtils
program ArrayUtilsDemo; {$APPTYPE CONSOLE} {$IFDEF FPC}
{$MODE DELPHI} {$ENDIF}
uses uIntArrayUtils;
const n=16;
var data:array[0..n-1] of Integer; item_count:Integer;
begin
//заполняем массив случайными значениями GenArray(data, Low(data), High(data), 1000);
//печатаем первые 20 элементов массива
if n>20 then item_count:=20 else item_count:=n; Writeln('Source array');
PrintArray(Slice(data, item_count)); Writeln;
Writeln;
//сортировка массива
QuickSort(data, Low(data), High(data));
//вывод результата (первые 20 элементов) Writeln('Sorted array'); PrintArray(Slice(data, item_count)); Writeln;
Writeln('Print Enter to Exit'); Readln;
end.