Немного о проектировании приложений
Понятно, что проектирование программного обеспечения это достаточно важный этап разработки, на котором необходимо продумать архитектуру будущего приложения, выбрать, например шаблон проектирования, продумать модель данных, типы и схемы базы данных, методы работы с данными, возможные варианты реализации API, варианты внешнего вида приложения и т.п. В материалах ниже будут приведена информация которая призвана помочь сделать такую работу более правильной.
Одним из шаблонов, о котором хочется упомянуть является MVC — это шаблон программирования, который позволяет разделить логику приложения на три части:
-
Model (модель) — получает данные от контроллера, выполняет необходимые операции и передаёт их в вид.
-
View (вид или представление) — получает данные от модели и выводит их для пользователя.
-
Controller (контроллер) — обрабатывает действия пользователя, проверяет полученные данные и передаёт их модели.
Такой подход позволяет разделить код на логические блоки и упростить его поддержку и развитие в будущем. Кроме того, это позволит быстро создавать различные реализации интерфейса пользователя, например обеспечивать параллельную работу как десктопного, так и WEB интерфейса, поскольку логика работы с данными не завязана на интерфейс программы.
По реализации MVC подхода в среде Delphi существует отличный цикл статей на habr.com, с которым рекомендуется ознакомиться:
-
MVC-подход к разработке пользовательских интерфейсов в Delphi. Часть 1. Галочка
-
MVC-подход к разработке пользовательских интерфейсов в Delphi. Часть 2. Списки
-
MVC-подход к реализации пользовательского интерфейса в Delphi. Часть 3. Объекты
Цикл статей, представленный выше, хорошо раскрывает основные аспекты темы и детально описывает методы, которые можно реализовать и в среде Lazarus.
Значительно улучшить структуру и функциональность вашего проекта может правильная организация модели данных и в этом должны помочь базовые возможности языка программирования и весь его арсенал по работе с синтаксисом с типами данных.
Ниже приведена информация, дополняющая и расширяющая материалы, разработанные Michalis Kamburelis — Приемы работы в современном Object Pascal, и призванная помочь более качественно организовать работу с моделью данных, разрабатываемого приложения.
Синтаксис и типы данных
Полезные директивы компилятора
{$modeswitch advancedrecords}
— разрешает использование записей с методами.
{$modeswitch typehelpers}
— разрешает использования хелперов для типов.
{$VarPropSetter+}
—
{$codePage UTF-8}
— позволяет выводить в консоль кириллические символы.
Подробную информацию о директивах можно получить на странице Директивы компилятора. Руководство программиста Free Pascal (freepascal.ru)
Литералы (Строковые ресурсы)
В контексте программирования на Pascal, литералы представляют собой данные, прямо записанные в код программы, и могут быть числами, строками, символами. Чрезмерное использование строковых литералов приводит к появлению большого количества данных в коде, которое при возникновении каких-либо изменений сложно поддерживать и исправлять.
Одно из возможных решений — вынесение литералов в отдельный файл настроек, который будет находиться рядом с программой, что позволит изменить данные без необходимости пересоборки программы. Но это потребует дополнительных усилий на обеспечение работы с таким файлом.
В библиотеках Free Pascal есть готовое решение для данной темы — Строки ресурсов. Подробную информацию о работе со строками ресурсов можно получить на странице Строки ресурсов. Руководство программиста Free Pascal.
Перечисляемые типы и наборы
Перечисляемый тип в большинстве случаев используется для того, чтобы перечислить какие-то признаки или определенные варианты значения свойства поля.
TAnchorKind = (akTop, akLeft, akRight, akBottom);
Кроме самого перечисляемого типа есть возможность создавать наборы, которые позволяют составлять различные комбинации значений из определенного перечисления. Это можно использовать для указания, вариантов допустимых или используемых опций. В качестве примера использования можно привести свойство Anchors любого компонента.
TAnchors = set of TAnchorKind;
Наличие выбранного варианта для перечисляемого типа удобно проверять в условии через оператор in
... myAncor:=akRight; Options:=[akTop, akLef]; if myAncor in Options then Writeln('myAncor in Options - true') else Writeln('myAncor in Options - false'); if myAncor in [akLeft,akRight] then Writeln('myAncor in') else Writeln('myAncor not in'); ...
Получить числовое значение для перечисления можно с помощью ORD
i:=ORD(myAncor);
Получить текстовое значение для перечисляемого типа можно с помощью специального оператора из модуля Typinfo
uses Typinfo; ... str:=GetEnumName(TypeInfo(TAnchorKind),ORD(myAncor))); ...
Для перечисляемого типа можно использовать Хелперы, что значительно повышает удобство работы с таким типом данных, Примеры Хелперов для перечисляемого типа будут приведены далее в разделе Хелперы (Helpers)
Местные типы, переменные, константы, процедуры и функции
Стоит отметить что внутри большой процедуры или функции, можно определить не только переменные, но и какой-то местный тип (структуру), вложенную (местную) под-процедуру или константы. Они будут видимы и могут использоваться в рамках большой процедуры или функции в которой определены.
Местные типы
В процессе разработки функций, например, для создания строковых таблиц с последующим выводом в файл или в виде графического изображения, мы можем столкнуться с необходимостью хранения информации в числовом формате, для выполнения расчетов или преобразований. В таких случаях удобно использовать локальные типы данных. В рамках разрабатываемой функции возможно объявить структуру, которая послужит основой для хранения данных одной строки, которую затем можно использовать как элемент массива, который в свою очередь и будет представлять собой данные таблицы. Внутри структуры могут быть определены различные записи, необходимые для расчётов, и они могут быть как строковыми, так целочисленными или вещественными.
Ниже приведен пример локальной функции формирующей таблицу для сохранения в файл.
Пример процедуры вывода таблицы на чертеж
// Процедура вывода таблицы на чертеж на основе начальных данных procedure CreateTable(AInitialData:TData); const // Местная константа ColCount = 4; type // Местная структура для хранения числовых данных TLocalStructure = record InPortContacts: integer; OutPortContacts: integer; TotalContactsOnType:integer; end; var // Двумерный строковый массив Grid: array of array of string; // Массив для хранения расчетных данных LocalData: array of TLocalStructure; Block: TBlock; i, j: integer; begin // Инициация массива табличных данных +1 - на заголовок SetLength(Grid,ColCount,AInitialData.Count+1); // Инициализация массива местной структуры SetLength(Grid,LocalData,AInitialData.Count); // Формирование заголовка таблицы Grid[0,0]:='№'; Grid[1,0]:='Тип'; Grid[2,0]:='Число входных портов в типе'; Grid[3,0]:='Число контактов на входной порт'; Grid[4,0]:='Число выходных портов в типе'; Grid[5,0]:='Число контактов на выходной порт'; Grid[6,0]:='Всего число контактов на тип'; // Анализ данных и заполнение строкового массива i:=0; for Block in AInitialData do with LocalData[i] do begin // Установка кол-ва контактов в зависимости от типа порта if Block.Type_='A' then begin InPortContacts:=2; OutPortContacts:=3; end else begin InPortContacts:=4; OutPortContacts:=5; end; // Выполнение необходимых расчетов TotalContactsOnType:=Block.InPorts.Count*InPortCotact+Block.OutPorts.Count*OutPortContacts; inc(i); // Заполнение строки таблицы Grid[0,i]:=i.ToString(); Grid[1,i]:=Block.Type_; Grid[2,i]:=Block.InPorts.Count.ToString(); Grid[3,i]:=InPortCotact.ToString(); Grid[4,i]:=Block.OutPorts.Count.ToString(); Grid[5,i]:=OutPortContacts.ToString(); Grid[6,i]:=TotalContactsOnType.ToString(); end; for i:=0 to Length(LocalData)-1 do begin // Здесь может производиться дополнительная обработка или вычисления для строки итогов end; // Здесь может располагаться код для вывода StringArray в файл end;
Местные функции
В ситуациях, когда внутри одной функции присутствуют похожие блоки кода, чтобы избежать дублирования, возможно создать местную (вложенную) функцию или процедуру, которая будет выполняться только в рамках текущей большой функции или процедуры/
Ниже приведен простой пример кода реализации вложенной функции:
... function One:integer; function SubOne(a:integer):integer; begin result:=a*10; end; var i,j:integer begin i:=SubOne(1); j:=SubOne(2); result:=i+j; end; ...
Условный оператор (If)
Во Free Pascal нет тернарного оператора, но есть альтернативные возможности реализации подобной логики.
Оператор ORD
Можно использовать функцию ORD(Boolean). Эта функция преобразует логическое значение (True или False) в целое число, где ORD(True) равно 1, а ORD(False) равно 0. Это может быть полезно, например, при умножении числа на логическое значение. Например, если мы хотим получить значение переменной x:-1
только в случае, если условие flag
истинно, мы можем использовать следующий код:
x:=1-2*ORD(flag);
В примере если flag:=true
, то x=-1
, а если flag:=false
, то x=1
.
Словарь
Для определения вариантов значений которые будут получены при том или ином значении переменной типа boolean
можно использовать словарь, как в коде ниже:
const direction:array[false..true] of integer =(-10,10); var flag:boolean; i:integer; begin flag:=false; i:=direction[flag]; i+=100; i+=100; end;
Функция из модуля math
Для числовых значений можно использовать функцию из модуля math,
function IfThen(val:boolean;const iftrue:integer; const iffalse:integer= 0) :integer;
Несколько условий в операторе If
В случае, когда нужно получить доступ к полю объекта, который на момент запроса может быть еще не инициирован, при простом обращении может возникнуть ошибка. Чтобы избежать остановки выполнения программы, можно использовать конструкцию Try Except
, а можно просто выполнить множественную проверку, когда в операторе if объединяются несколько условий.
При объединении условий если первое условие не выполняется, то последующие условия не проверяются. В примере ниже функция two
не будет вызвана. Дополнительно стоит обратить внимание на то что компилятор в первую очередь будет пытаться выполнить побитовый оператор and
и в случае записи кода условия следующим образом: if one and two then
— будет выходить ошибка, поэтому при объединении условий их необходимо брать в скобки.
program Project1; function one:boolean; begin result:=false; end; function two:boolean; begin result:=true; end; begin if (one) and (two) then begin writeln('and'); end; end.
По аналогии с кодом, приведенным выше, в первом условии выполняется проверка, инициирован ли определенный объект if (Assigned(SomeObject)
, во втором условии, допустим проверяется, данные в каком либо поле, тогда общая запись будет иметь вид if (Assigned(SomeObject)) and (SomeObject.Value <> 0) then
. Такая конструкция с несколькими проверками делает код более читаемым и простым.
Оператор (in) для условий
Для перечислений удобно проверять наличие значения в наборе через оператор in
if (TmyEnum in [meApple, meBanana]) then begin end;
Оператор in можно определить для своей структуры и использовать уже его для проверок
uses SysUtils; type TDisk = class; TScheme = specialize TObjectList; operator in (const A: TDisk; const B: TScheme):boolean; begin if not (B is TScheme) then raise Exception.Create('TScheme expected'); Result := B.IndexOf(A) >= 0; end;
Оператор цикла (for)
Помимо классической организации цикла for при которой задается начальное значение, условие продолжения и шаг итерации возможно использовать цикл for item in items
, который позволяет перебирать элементы коллекции (например, массива или списка) без явного указания индексов.
... var myArray: array[1..5] of Integer; item: Integer; // Заполнение массива for item in myArray do begin //операции с элементами массива end;
В случаях когда нужно обойти коллекцию с использованием индекса, начиная с последнего элемента до первого, то вместо того чтобы вычитать 1 из items.count
, мы можем использовать функцию Pred
, которая возвращает предыдущее значение. Пример:
var i: Integer; for i := Pred(items.count) downto 0 do begin // Ваш код здесь end;
О чем еще не сказано
В части 2. статьи «Приемы работы в современном Free Pascal» будут приведены приемы работы со структурами и классами.
ссылка на оригинал статьи https://habr.com/ru/articles/867968/
Добавить комментарий