Пусть требуется разработать программу, которая должна рассматривать точки на плоскости и находить среди них пару (одну), расстояние между которыми максимально.
Похоже, что это самое крупное структурное разбиение, потому что если какую то пару блоков объединить, то объединенный блок будет выполнять не одну, а две функции.
Примечание: В таком соединении иногда есть смысл. Например, ввод данных объединить с какими-то вычислениями, потому что при вводе с клавиатуры процессор почти все время свободен.
Если выделенные блоки могут применяться не только в разрабатываемой программе, но и в каких-то других программах или они должны исполняться несколько раз в программе (или и то и другое), то такие блоки обычно оформляют в виде подпрограмм. Мы так и сделаем. Описание таких вспомогательных подпрограмм обычно размещают в отдельном модуле, чтобы структура главного модуля была максимально ясной. Применение модулей имеет и другие достоинства.
А где же главный модуль и что он делает ?
Главный модуль обеспечивает интерфейс пользователя, например, на главной форме разместим переключатель для выбора вариантов получения исходных данных, переключатель для выбора варианта просмотра данных и варианта представления результатов. Полезно предусмотреть кнопку "Пуск" и кнопку для выхода (завершения работы). Конечно, что-нибудь забыто, но надеемся дополнить в ходе работы
Через некоторое время мне это не понравилось, решил, что на главной форме для вызова подпрограмм должно быть просто главное меню. И что можно добавить ввод и вывод данных с использованием файла (текстового, каждая строка - координаты точки).
Так примерно выглядит стиль разработки "сверху - вниз", т е вначале определяем крупные структурные элементы и их функции. После этого разработку каждого блока можно поручить отдельному программисту, и это ускорит создание проекта в целом. Однако, реально не хватает одного момента - нужно договориться реализовывать каждый блок в виде подпрограммы и определить заголовки (т е список параметров) для подпрограмм. Как ни странно, можно спроектировать заголовки подпрограмм уже сейчас, хотя, конечно, можно кое-что не угадать и тогда придется корректировать. Итак, приступим.
Думаю, что этап разработки заголовков подпрограмм был не лишним. Теперь
нужно выбрать стиль продолжения разработки. Можно сосредоточиться на детальной разработке всех (по очереди) или некоторых подпрограмм, а затем организовать их вызов из главной формы. А можно действовать наоборот: вначале создать главное меню и обеспечить вызовы подпрограмм (которые пока заменить заглушками), а потом заняться детальной разработкой подпрограмм, начиная с тех, которые можно проверять и отлаживать не дожидаясь готовности остальных. Отметим, что заголовки заглушек совпадают с заголовками "настоящих" подпрограмм, т е являются окончательными, меняется только тело (т е: begin -операторы- end). Для этой программы выберу 2-й стиль.

procedure InputFromKeyboard(NumPoints: integer; // число точек
MinX,MaxX,MinY,MaxY: double; // допустимые мин и макс значения координат.
var Coord: TCoord { массив координат точек.
Тип TCoord (TCoord = array of TPointFloat) не забудьте описать глобально } );
begin
showmessage('Ввод с клавиатуры');
end;
Так как эта процедура должна быть доступна в модуле unMain, то ее заголовок помещаю в разделе interface модуля unPokaz (копирую заголовок). Выше его ставлю описание типа TCoord.
Для того, чтобы эти описания были доступны в unMain, нужно в предложение uses этого модуля добавить unPokaz. Пока что эти описания используются только в части implementation, поэтому достаточно добавить в этой секции. Здесь же описываю переменные NPoints, MinX1, MaxX1, MinY1, MaxY1, Coord1.
unit unMain;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, Menus;
type
TfrmMain = class(TForm)
MainMenu1: TMainMenu;
Vvod: TMenuItem;
KeyBoard: TMenuItem;
fromFile: TMenuItem;
Random: TMenuItem;
RG1: TRadioGroup;
OD1: TOpenDialog;
SD1: TSaveDialog;
SaveAs: TMenuItem;
Solve: TMenuItem;
Quit: TMenuItem;
Pokaz: TMenuItem;
procedure KeyBoardClick(Sender: TObject);
procedure fromFileClick(Sender: TObject);
procedure RandomClick(Sender: TObject);
procedure SaveAsClick(Sender: TObject);
procedure QuitClick(Sender: TObject);
procedure PokazClick(Sender: TObject);
procedure SolveClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
implementation uses unPokaz;
{$R *.dfm}
var NPoints, NN1, NN2: integer;
MinX1,MaxX1,MinY1,MaxY1: double;
Coord1: TCoord;
FName: string;
procedure TfrmMain.KeyBoardClick(Sender: TObject);
begin
InputFromKeyboard(NPoints,MinX1,MaxX1,MinY1,MaxY1,Coord1);
RG1.Enabled := True;
Solve.Enabled:= true;
end;
procedure TfrmMain.fromFileClick(Sender: TObject);
begin
if OD1.Execute then
begin
FName:= OD1.FileName;
DataFromFile(NPoints,MinX1,MaxX1,MinY1,MaxY1,Coord1,
FName);
end;
RG1.Enabled := True;
Solve.Enabled:= true;
end;
procedure TfrmMain.RandomClick(Sender: TObject);
begin
RandomData(NPoints,MinX1,MaxX1,MinY1,MaxY1,Coord1);
RG1.Enabled := True;
Solve.Enabled:= true;
end;
procedure TfrmMain.SaveAsClick(Sender: TObject);
begin
if SD1.Execute then
begin
FName:= SD1.FileName;
SaveData(NPoints, Coord1, FName);
end;
end;
procedure TfrmMain.QuitClick(Sender: TObject);
begin
Application.Terminate;
end;
procedure TfrmMain.PokazClick(Sender: TObject);
begin
case RG1.ItemIndex of
0: ShowTabPoints(NPoints,MinX1,MaxX1,MinY1,MaxY1,Coord1);
1: ShowGraphPoints(NPoints,MinX1,MaxX1,MinY1,MaxY1,Coord1,0,0);
end {case};
end;
procedure TfrmMain.SolveClick(Sender: TObject);
begin
CalcMax(NPoints,MinX1,MaxX1,MinY1,MaxY1,Coord1,NN1,NN2);
ShowGraphPoints(NPoints,MinX1,MaxX1,MinY1,MaxY1,Coord1,NN1,NN2);
end;
end.
======================================================
unit unPokaz;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TfrmPokaz = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmPokaz: TfrmPokaz;
type TPointFloat = record
X,Y: single;
end;
TCoord = array of TPointFloat;
procedure InputFromKeyboard(NumPoints: integer; // число точек
MinX,MaxX,MinY,MaxY: double; // допустимые мин и макс значения координат.
var Coord: TCoord { массив координат точек.
Тип TCoord не забудьте описать глобально } );
procedure DataFromFile(var NumPoints: integer; var MinX,MaxX,MinY,MaxY: double;
var Coord:TCoord; FName: string {имя файла с данными});
procedure RandomData(NumPoints: integer; MinX,MaxX,MinY,MaxY: double;
var Coord: TCoord);
procedure CalcMax(NumPoints: integer; MinX,MaxX,MinY,MaxY: double;
var Coord: TCoord; var N1,N2: integer {номера заданных точек});
procedure SaveData(var NumPoints: integer; var Coord: TCoord;
FName: string {имя файла с данными});
procedure ShowTabPoints(NumPoints: integer; MinX,MaxX,MinY,MaxY: double;
var Coord: TCoord);
procedure ShowGraphPoints(NumPoints: integer; MinX,MaxX,MinY,MaxY: double;
var Coord: TCoord; N1,N2: integer);
implementation
{$R *.dfm}
procedure InputFromKeyboard(NumPoints: integer; // число точек
MinX,MaxX,MinY,MaxY: double; // допустимые мин и макс значения координат.
var Coord: TCoord { массив координат точек.
Тип TCoord не забудьте описать глобально } );
begin
showmessage('Ввод с клавиатуры');
end;
procedure DataFromFile(var NumPoints: integer; var MinX,MaxX,MinY,MaxY: double;
var Coord:TCoord; FName: string {имя файла с данными});
begin
showmessage('Ввод из файла');
end;
procedure RandomData(NumPoints: integer; MinX,MaxX,MinY,MaxY: double;
var Coord: TCoord);
begin
showmessage('Случайные данные');
end;
procedure CalcMax(NumPoints: integer; MinX,MaxX,MinY,MaxY: double;
var Coord: TCoord; var N1,N2: integer {номера найденных точек});
begin
showmessage('Вычисляю...');
end;
procedure SaveData(var NumPoints: integer; var Coord: TCoord;
FName: string {имя файла с данными});
begin
showmessage('Сохраняю...');
end;
procedure ShowTabPoints(NumPoints: integer; MinX,MaxX,MinY,MaxY: double;
var Coord: TCoord);
begin
showmessage('Показ таблицы');
end;
procedure ShowGraphPoints(NumPoints: integer; MinX,MaxX,MinY,MaxY: double;
var Coord: TCoord; N1,N2: integer);
begin
showmessage('Показ точек - график');
end;
end.
procedure RandomData(NumPoints: integer; MinX,MaxX,MinY,MaxY: double;
var Coord: TCoord);
var ii: integer;
begin
// showmessage('Случайные данные');
Randomize; // инициализируется генератор случайных чисел. без этого
// последовательность случайных чисел будет одинакова
// при всех запусках процедуры.
for ii:=0 to NumPoints-1 do
begin
Coord[ii].X:= MinX + (MaxX-MinX)*random;
Coord[ii].Y:= MinY + (MaxY-MinY)*random;
end;
end;
Метод обработки клика по пункту меню "Случайные данные" тоже
дополнен и имеет вид:
procedure TfrmMain.RandomClick(Sender: TObject);
begin // на форму frmMain добавлены TEdit - для ввода данных
NPoints:= StrToInt(edCount.Text);
MinX1:= StrToFloat(edminX.Text);
MaxX1:= StrToFloat(edmaxX.Text);
MinY1:= StrToFloat(edminY.Text);
MaxY1:= StrToFloat(edmaxY.Text);
if MaxX1 <= MinX1 then
begin
showmessage('MaxX > MinX !');
exit;
end;
if MaxY1 <= MinY1 then
begin
showmessage('MaxY > MinY !');
exit;
end;
SetLength(Coord1,NPoints); // выделяется память массиву Coord1
RandomData(NPoints,MinX1,MaxX1,MinY1,MaxY1,Coord1); // вызов процедуры
RG1.Enabled := True; // меняем доступность некоторых компонент.
Solve.Enabled:= true;
edCount.Enabled:= false;
edminX.Enabled:= false;
edmaxX.Enabled:= false;
edminY.Enabled:= false;
edmaxY.Enabled:= false;
end;
procedure ShowGraphPoints(NumPoints: integer; MinX,MaxX,MinY,MaxY: double;
var Coord: TCoord; N1,N2: integer);
var ii,XX,YY: integer;
ax,bx,ay,by: double;
{ коэффициенты линейного преобразования реальных координат
в экранные: Хэкр := ax*Хреал + bx; такие, что
Хэкр(Xmin) = 0; Хэкр(Xmax) = ширина формы.
Yэкр := ay*Yреал + by;
Yэкр(Ymin) = высота формы; Yэкр(Ymax) = 0.
a,b - определяются из решения СЛАУ, записанных выше
}
begin
// showmessage('Показ точек - график');
ax:= frmPokaz.ClientWidth/(MaxX-MinX);
bx:= ax*MinX;
ay:= frmPokaz.ClientHeight/(MinY - MaxY);
by:= -ay*MaxY;
with frmPOkaz do
begin
Canvas.Pen.Width:=2;
Canvas.Pen.Color:= clGreen;
Color:= clBlack;
Canvas.FillRect(ClientRect);
Show;
for ii:= 0 to NumPoints - 1 do
begin
XX:= Round(ax*Coord[ii].X + bx);
YY:= Round(ay*Coord[ii].Y + by);
Canvas.Pixels[XX,YY ]:= RGB(200,200,200);
Canvas.Pixels[XX+1,YY ]:= RGB(200,200,0); //чтобы усилить точку
Canvas.Pixels[XX+1,YY+1 ]:= RGB(0,200,200); //чтобы усилить точку
Canvas.Pixels[XX,YY+1 ]:= RGB(200,0,200); //чтобы усилить точку
end;
if N1<>N2 then
begin
XX:= Round(ax*Coord[N1].X + bx);
YY:= Round(ay*Coord[N1].Y + by);
Canvas.Ellipse(XX-5,YY-5,XX+5,YY+5);
XX:= Round(ax*Coord[N2].X + bx);
YY:= Round(ay*Coord[N2].Y + by);
Canvas.Ellipse(XX-5,YY-5,XX+5,YY+5);
end;
end;
end;
procedure CalcMax(NumPoints: integer; MinX,MaxX,MinY,MaxY: double;
var Coord: TCoord; var N1,N2: integer {номера найденных точек});
var ii,jj,imax,jmax: integer; Rasst,RMax: double;
begin
// showmessage('Вычисляю...');
RMax:= 0;
for ii:=1 to NumPoints-1 do //цикл перебирает всевозможные пары точек
for jj:= ii+1 to NumPoints do // и находит максимальное расстояние
begin
Rasst:= sqrt(sqr(Coord[ii-1].X - Coord[jj-1].X)
+ sqr(Coord[ii-1].Y - Coord[jj-1].Y));
if Rasst > Rmax then
begin
RMax:= Rasst;
imax:= ii-1;
jmax:= jj-1;
end;
end;
N1:= imax; // выходные параметры заполняю.
N2:= jmax;
end;