Кроссплатформенный квест

Читать на сайте автора.

Заначка

 — что-либо прибережённое, припрятанное про запас ◆ Опять же всё расставив по местам, как и было, достал из-за трюмо тёткину заначку — вскрытую пачку «Любительских», — закурил. Андрей Битов, «Сад», 1960–1963 г. (цитата из Национального

http://alexander-bagel.blogspot.com/2014/11/store.html

Общее оформление файла

Эта заметка является продолжением к заметкам Стиль оформления кода. Вместо вступления, Настройки окружения и Регистр букв.

(Я планировал немного другой порядок публикаций, но отсутствие времени (ну т.е. лень) не даёт мне возможности навести порядок в некоторых промежуточных моментах.)

Копирайт

Авторские права исходных текстов, разрабатываемых внутри компании, принадлежат самой компании и лицензируются согласно правилам лицензирования, установленных внутри компании.

Следует разделять два уровня доступа к исходным текстам:

  • внутри компании (при создании и сопровождении проектов);
  • вне компании (при публикации в открытый доступ, либо при передаче третьим лицам).

Размещение копирайта в исходном тексте модулей в первом случае не нужно (это трудоёмко и снижает читаемость), но является необходимым во втором случае. Это делается непосредственно перед публикацией/передачей (либо при размещении в системе контроля версий) автоматизировано, необходимый текст генерируется по шаблону и вставляется в каждый исходный файл в самом начале в виде комментария. Кроме копирайта, такой комментарий может содержать информацию о лицензионном соглашении, номер версии (или ревизии) файла, историю изменений и другую информацию.

Начало

В зависимости от своего типа, файл начинается с одного из зарезервированных слов: package, program, library или unit. Следом за ним, через один пробел, следует имя файла (без расширения) с точкой с запятой на конце. Например:

unit UnitName;

В случае отсутствия копирайта, такая строка должна быть самой первой в файле, либо отстоять через одну пустую строку после копирайта.

Это требование исходит из соображений: в случае последовательного просмотра файлов из «продвинутых» файловых менеджеров — имя файла всегда находится на одном и том же уровне от начала файла, а в случае отсутствия копирайта переход к имени модуля осуществляется всего в три клавиши: Ctrl+Home, Ctrl+Right.

Если файл имеет неявную для компилятора связь с другим текстовым файлом, который обрабатывается до (либо после) компиляции, то следующей строкой может идти комментарий вида:

// UnitName.xml

Это удобно для быстрого перехода к редактированию указанного в комментарии файла по сочетанию Ctrl+Enter.

Информацию о версии, даты создания и модификации, а также историю изменений можно вести в отдельном текстовом файле, сославшись на него аналогичным образом:

// UnitName.hst

Однако это является излишнем при наличии системы контроля версия с обязательными комментариями при коммитах.

Далее может следовать многострочный комментарий с описанием предназначения файла и нюансы его использования. Текст комментария обрамляется либо фигурными скобками { и }, либо парой (* и *) и отстоит от левого края на один отступ (см. пример). Однако вместо такого комментария рекомендуется использовать отдельный текстовый файл и его имя размещать в простом комментарии, например:

// UnitName.txt

После всех комментариев следуют директивы компилятора, относящиеся ко всему исходному тексту модуля. См. также: определения условной компиляции.

Секция uses

Зарезервированное слово uses занимает отдельную строку. Объявляемые далее модули группируются по принадлежности, а внутри группы располагаются в порядке использования (см. пример).

В секции implementation слово uses следует располагать после директив, относящихся ко всему разделу реализации модуля (см. пример).

Эта рекомендация следует из соображения, что секция uses может быть многострочной и имеет тенденцию к расширению. Съезжающие вниз директивы отвлекают внимание.

Секция interface

Все объявления из интерфейсной секции модуля доступны другим модулям и становятся глобальными. Поэтому секция interface должна быть максимально компактной и лаконичной, и не должна содержать объявления, которые не могут или не должны использоваться за пределами модуля. Это в особенности касается переменных, создаваемых автоматически при создании новых форм/фрейм:

var
  Form1: TForm1;

Такие переменные (за исключением единичных случаев) следует сразу удалять.

В случае, когда модуль является служебным и его код исполняется во время инициализации и/или финализации, интерфейсная часть модуля может быть пустой.

Секция implementation

Это секция реализации модуля. Она может быть пустой, либо содержать только код инициализации и/или финализации — это секции initialization и finalization.

Конец модуля

Модуль заканчивается ключевым словом end с точкой на конце. Размещение любого текста после последнего end не запрещено, но приводит к выдаче компилятором предупреждения, потому считается крайне не желательным.

Директивы компилятора

Директивы компилятора, относящиеся ко всему исходному тексту модуля, располагаются перед интерфейсной частью между ключевыми словами unit и interface. Директивы, относящиеся к секции реализации модуля, располагаются сразу за словом implementation. Остальные директивы могут располагаться в тексте в любом месте, согласно правилам форматирования кода. (Также см. пример.)

Порядок объявления методов

В VCL принято сначала объявлять конструктор класса, затем деструктор, затем все методы в алфавитном порядке. Это удобно, когда реализуемый класс получается большим (содержит большое количество методов) и является законченным и готовым к использованию. Однако алфавитный порядок следования методов неудобен при чтении кода и при его сопровождении. Для удобства рекомендуется подход, описанный ниже.

При объявлении методов (в секции interface) сначала объявляются обработчики методов. Обработчики группируются по принципу их принадлежности — сначала обработчики формы (фрейма), затем обработчики остальных компонентов (Action’ов, кнопок, грида и т.п.). Внутри группы рекомендуется использовать порядок в соответствии порядка использования обработчиков в RunTime, т.е. сначала идут OnCreate, затем OnShow (или OnInit, OnLoadData, OnLoadState), а затем OnClose, OnDestroy (OnSaveState, OnSaveData, OnDone).

«Парные» методы можно располагать рядом, т.е. обработчики OnCreate/OnDestroy, OnShow/OnClose и т.п. Это удобно для контроля парности действий, выполняемых в таких обработчиках.

Для обработчиков событий Action’ов — сначала OnUpdate, затем OnExecute. Это положение вытекает из таких моментов:

  • Action.Execute всегда внутри себя сначала вызывает OnUpdate, а затем OnExecute, следование обработчиков в коде логично сопоставить с порядком вызова обработчиков. (Кстати, это документированная особенность TAction — обработчик OnExecute не будет вызван, если в OnUpdate свойство Action.Enabled сбрасывается в False.)
  • Читая код OnExecute часто приходится на какой-то момент переключаться на код обработчика OnUpdate, для глаз это удобнее делать, когда расстояние между обработчиками (ключевыми словами procedure) фиксированное. Как правило, код обработчика OnUpdate занимает одну-две строки, а код OnExecute может занимать и более 10 строк. Расположение обработчиков в порядке OnUpdate/OnExecute повышает скорость читаемости кода.

Остальные методы (которые не являются обработчиками событий) также рекомендуется группировать по смыслу и располагать в порядке их использования.

При реализации методов (в секции implementation), рекомендуется соблюдать те же принципы, что и при объявлении, единственным отличием может быть тот факт, что некая группа методов в разделе объявления может быть разделена на части по области видимости.

Пример

unit Example;
// Example.hst

(*
  Необязательное описание предназначения файла и нюансы его использования.
  Вместо данного описания рекомендуется давать понятные имена файлам, чтобы предназначение
  было самим-собой разумеющимся, а нюансы использования вытекали автоматически из интерфейсной части модуля.
  А в случае "раздувания" такого комментария его рекомендуется вынести в отдельный текстовый файл.
*)

{$I Common.inc}
{$ALIGN ON}
{$MINENUMSIZE 4}

interface

uses
  // Delphi units
  SysUtils, DateUtils, Windows, Classes, Forms,
  // My units
  MyUnit1, MyUnit2;

type
  TExample = class(TForm)
    procedure FormLocalize(Sender: TObject);
    // OnCreate, OnDestroy, OnShow, OnClose ...
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

implementation

{$R *.dfm}

uses
  MyUnitA, MyUnitB;

{ TExample }

constructor TExample.Create(AOwner: TComponent);
begin
  inherited;
  // do something
end;

destructor TExample.Destroy;
begin
  // do something
  inherited;
end;

procedure TExample.FormLocalize(Sender: TObject);
begin
  {$include *.mui.inc}
end;

// OnCreate, OnDestroy, OnShow, OnClose ...

end.

Читать на сайте автора.

Регистр букв

Эта заметка является продолжением к заметкам Стиль оформления кода. Вместо вступления и Настройки окружения.

Регистр букв

Во время кодирования, при выборе регистра букв, необходимо соблюдать следующие правила:

  • все зарезервированные слова и директивы пишутся в нижнем регистре;
  • при повторном использовании идентификатора используется тот вариант написания, который использовался при первом объявлении этого идентификатора.

Во время проектирования, при составлении новых идентификаторов, необходимо руководствоваться общим правилом (см. ниже), а также соглашением об именовании (планируется отдельной заметкой).

Особым образом пишутся директивы компилятора (см. ниже).

Общее правило для идентификаторов

При написании новых идентификаторов необходимо использовать стиль CamelCase, в котором каждая осмысленная часть идентификатора начинается с заглавной буквы, а символы подчёркивания (для соединения частей) не используются. Примеры и допустимые отступления от этого стиля представлены в таблице ниже.

HINT: соглашение об именовании может содержать исключения из этого правила. Например идентификаторы вида: WM_ACTIVATE, DWORD, BITMAPFILEHEADER, some_table_filed и другие.

Если наименование идентификатора

то

Пример

1

состоит из одной буквы

допускается любой регистр

i, j, k: Integer;
s, t, u: string;
C: Cardinal;
D: TDateTime;

2

состоит из одного слова (в том числе аббревиатуры и сокращения)

начинается с заглавной буквы

Count
Value
Next
Rtti
Vcl
Db
Eof
Tmp
Src
Dst
Cnt

3

состоит из нескольких слов

слова пишутся слитно, каждое с заглавной буквы

FirstChar
DoSomethingElse
RttiContext
ItemsCnt

4

начинается с префикса, указывающего на тип идентификатора

префикс пишется строчными буквами

poScreenCenter
fsModal

btnOk
frmMain
acSetup

5

начинается с буквы, указывающей на вид идентификатора

буква пишется в верхнем регистре

SResourceString
EMyException
ISomeInterface
TAnotherType
FClassField
AMethodParameter
LLocalVariable

Директивы компилятора

Выбор регистра букв при написании директив компилятора сводится к простому правилу:

  • все однобуквенные директивы всегда записываются в верхнем регистре;
  • директивы, влияющие на текст компилируемого кода, записываются в нижнем регистре;
  • все остальные директивы — записываются в верхнем регистре.

Параметры всех директив пишутся в соответствии с общим правилом (см. выше).

Включение части кода из другого файла

Для включения (вставки) текста из указанного файла в текущий фрагмент кода при компиляции используется директива {$include} или {$I}.

Если включаемый файл содержит набор директив для компилятора и не содержит «полезный» код, следует использовать краткую форму написания в верхнем регистре:

{$I OurCompanyDefinitions.inc}

В противном случае используется полная форма, и записывается в нижнем регистре:

{$include SomeFileWithCode.inc}

Условная компиляция

Директивы условной компиляции кода, такие как {$define SomeSymbol}, {$ifdef SomeSymbol}, {$else}, {$endif} и т.п., пишутся в нижнем регистре.

См. также: определения условной компиляции.

Регионы

Редактор кода Delphi для удобства навигации позволяет сворачивать/разворачивать логические единицы исходного кода.

Для выделения произвольной части кода в сворачиваемую единицу используются регионы. Для этого необходимый текст обрамляется директивами {$region ‘Region Description’} и {$endregion}, которые пишутся в нижнем регистре. Описание региона (‘Region Description’) игнорируется компилятором, поэтому должно быть удобочитаемым и подчиняться правилам естественного языка.

Нижний регистр в написании этих директив является исключением из основного правила и следует из соображения, что они не должны бросаться в глаза и отвлекать на себя внимание.

Читать на сайте автора.