Скачиваний:
44
Добавлен:
29.03.2016
Размер:
1.43 Mб
Скачать

Разработка приложений с использованием модулей

Существует два различных подхода при разработке программы использующей модули.

При первом подходе модуль используется просто как библиотека подпрограмм, констант, типов и пр. Примером таких модулей могут служить рассмотренные ниже модули 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.

Соседние файлы в папке ЛР по программированию FreePASCAL