# Coding Standard

## Naming Conventions

Ориентироваться стоит на правила, указанные в [руководстве по стилю](https://docs.myrusakov.ru/unreal-engine/rukovodstvo-po-stilyu).

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

1. [Cropout Sample Project](https://www.unrealengine.com/en-US/blog/cropout-casual-rts-game-sample-project)
2. [Lyra Sample Game](https://docs.unrealengine.com/en-US/lyra-sample-game-in-unreal-engine/)

## Правила написания кода

1. Использовать ClangFormat.
2. Использовать модули (сделать правильный выбор между PublicDependencyModuleNames или PrivateDependencyModuleNames для добавления зависимости).
3. Весь код должен быть протестирован.

## Обработка ошибок

1. Для проверки корректности входных данных используются if, IsValid(), ensure, ensureMsgf. При этом ensure используется для проверки того, что должно проходить без ошибок за редчайшим исключением (например, не хватило памяти для инициализации компонента), а ensureMsgf для ошибок, которые мог допустить пользователь, а потому ему нужны понятные пояснения для исправления его ошибки.
2. Получив ошибку, начинать надо с написания теста, выявляющего эту ошибку.

## Порядок данных в заголовочном файле

1. Copyright
2. \#pragma once
3. \#include по алфавиту файлов движка
4. \#include по алфавиту файлов проекта
5. \#include FileName.generated.h, если необходим
6. Forward declaration по алфавиту типов движка (сначала структуры, потом интерфейсы и потом классы)
7. Forward declaration по алфавиту типов проекта (сначала структуры, потом интерфейсы и потом классы)
8. Делегаты
9. Перечисления
10. Структуры
11. Объявление класса

## Порядок переменных и функций в заголовочном файле

Сначала указываются все сущности public, затем protected и в конце private:

1. Переменные с макросом UPROPERTY (порядок: EditDefaultsOnly, EditAnywhere, BindWidget, VisibleAnywhere, все остальные)
2. Статические константы
3. Статические переменные
4. Обычные переменные
5. Делегаты
6. Конструкторы
7. Функции с макросом UFUNCTION()
8. Статические функции
9. Виртуальные функции (в порядке жизненного цикла объекта)
10. Обычные функции (в порядке жизненного цикла объекта)

## Порядок данных в файле реализации

1. Copyright
2. \#include заголовочного файла
3. \#include по алфавиту файлов движка
4. \#include по алфавиту файлов проекта
5. Функции в соответствующем порядке

## Порядок функций в файле реализации

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

Когда все эти функции определены, берётся следующая нереализованная функция из заголовочного файла после функции A().

## Правило обращения к переменным класса

Предпочтительно сначала использовать непосредственное обращение к переменной, пока оно не является препятствием. При возникновении неудобств этого подхода стоит переходить на косвенное обращение к переменным, то есть через get-функцию.

## Checklist для перечислений

* [ ] Название перечисления - существительное в единственном числе (например, EPRJGameState, а не EPRJGameStates)
* [ ] Для первого элемента значение равно 0

## Checklist для классов

* [ ] Класс не наследуется от другого класса
* [ ] Класс имеет основную цель
* [ ] Имя класса описывает основную цель
* [ ] Имя класса содержит специфичный для проекта префикс
* [ ] Класс имеет согласованную абстракцию
* [ ] Интерфейс класса ясно описывает его использование
* [ ] Интерфейс класса абстрактен настолько, что он может рассматриваться как "чёрный ящик"
* [ ] Класс разделён на необходимые компоненты (при необходимости)
* [ ] Члены класса сделаны минимально доступными
* [ ] Члены класса не имеют общих аффиксов
* [ ] Класс избегает предположений о своих клиентах, включая свои производные классы
* [ ] Класс не содержит компонентов, которые не используются в коде (например, какой-нибудь StaticMeshComponent, нужный только в Blueprint)
* [ ] Соблюдается правило IWYU (Include-What-You-Use)

## Checklist для методов

* [ ] Все части метода, которые целесообразно поместить в отдельные методы, сделаны отдельными методами
* [ ] Имя метода имеет вид "выразительный глагол + объект", либо описывает возвращаемое значение
* [ ] Имя метода не имеет избыточный контекст (например, если класс называется Character, то называть функцию GetCharacterName() в большинстве случаев бессмысленно, а лучше назвать её просто GetName())
* [ ] Обработчик делегата имеет вид \<object name>\<delegate purpose> (например, ButtonClicked)
* [ ] Если метод возвращает boolean, то имя функции должно содержать вопрос (Is, Has, Can, Was, Should)
* [ ] Метод решает только одну задачу, причём делает это хорошо
* [ ] Порядок параметров метода соответствует порядку параметров аналогичных методов
* [ ] Параметр имеет префикс Out, если параметр служит для записи в него значения
* [ ] В методе делаются проверки на некорректные входные данные
* [ ] Порядок проверок предусловий следующий: пользовательские настройки, поля класса, параметры функции
* [ ] Все указатели проверены на nullptr перед использованием
* [ ] Метод имеет не более 10-ти (лучше 5-ти) точек принятия решений (счёт начинается с 1, дальше прибавляется ещё 1 на каждом вхождении if, while, for, and, or)
* [ ] В lambda-выражениях не используется режим захвата по умолчанию
* [ ] Метод не имеет наблюдаемых побочных действий
* [ ] Максимальный уровень отступов не превышает двух
* [ ] Метод либо что-то делает, либо отвечает на вопрос, но не одновременно
* [ ] Метод соблюдает закон Деметры
* [ ] Метод содержит не более пяти строк кода, не считая { и }
* [ ] Метод вызывает методы в объекте, либо передаёт этот объект в виде аргумента, но не то и другое одновременно
* [ ] Метод не содержит пустых строк

## Checklist для переменных

* [ ] Имя переменной полно и точно описывает свою сущность
* [ ] Спецификаторы (Total, Sum, Average, Max, Min, Count) находятся в конце имени (например, ItemCount, а не CountItems (в первом варианте, ещё и "s" не нужна))
* [ ] Имя переменной не содержит цифр
* [ ] Если переменная - boolean, то она имеет префикс b
* [ ] Если переменная - boolean, то она не содержит в имени глагола
* [ ] Имя переменной не имеет избыточный контекст (например, если класс называется Character, то называть переменную CharacterName в большинстве случаев бессмысленно, а лучше назвать её просто Name)
* [ ] Имя переменной не включает имя атомарного типа
* [ ] Имя переменной включает имя неатомарного типа
* [ ] Время жизни переменной сокращено до минимума
* [ ] Переменная инициализирована сразу, либо, хотя бы, перед первым использованием
* [ ] Используется списочная инициализация
* [ ] Если переменная - указатель, то перед использованием она проверяется (за исключением переменных в конструкторе UObject)
* [ ] Если переменная - указатель, то используется интеллектуальный указатель
* [ ] Используется TObjectPtr, если это указатель в UPROPERTY, в противном случае - TObjectPtr не используется
* [ ] Указан диапазон допустимых значений и единица измерений в UPROPERTY, если эта переменная применяется для настройки в Blueprint
* [ ] Указана универсальная категория (например, Settings) с возможными подкатегориями в UPROPERTY, если эта переменная применяется для настройки в Blueprint
* [ ] Делегат имеет вид FOn\<object name>\<delegate purpose> (например, FOnButtonClicked)
* [ ] Переменная типа делегата имеет вид On\<object name>\<delegate purpose>, если она public
* [ ] Переменная типа делегата имеет вид On\<object name>\<delegate purpose>Event, если она protected или private

## Checklist по числам

* [ ] Код не содержит магические числа
* [ ] Все ошибки деления на ноль предупреждены
* [ ] Проблемы целочисленного переполнения в выражениях предупреждены
* [ ] Прямая проверка на равенство (через ==) чисел с плавающей запятой отсутствует
* [ ] Все индексы массивов проверяются перед использованием

## Checklist по условным операторам

* [ ] "Ненормальный" вариант обрабатывается отдельно, после чего идёт return
* [ ] В последовательности if-else-if вначале идут наиболее вероятные сценарии
* [ ] Условие if простое для понимания (при необходимости можно создать отдельную функцию с хорошим названием)
* [ ] Условный оператор использует {} даже для одного оператора внутри
* [ ] Блок else (или switch) может использоваться только на границе программы
* [ ] Инструкция if, если она есть, идёт только в начале метода

## Checklist по тестированию

* [ ] Тестовый код такой же чистый, как и код продукта
* [ ] Тестовый код делится на 3 части: построение тестовых данных, выполнение операций, проверка результатов
* [ ] В тесте проверяется только одна концепция
* [ ] ExpectedValue должен быть справа

## Checklist по виджетам

* [ ] Имя корневого элемента имеет вид RootТипЭлемента (например, RootCanvas)
