Про нейминг и организацию кода в шарпах и в лиспе

Все что ниже - по просьбе; мое личное мнение, под мое сегодняшнее видение ситуации. Может быть, через полгода я буду смотреть на это как на бред сумасшедшего. Какие бы ни были приняты правила - пускай даже самые дикие - их наличие значительно лучше их отсутствия.

На всякий случай - если будете кликать на картинках, открывайте их в новой вкладке.

Общие требования к коду
Код, который разрабатывается, должен… Должен что? Компилироваться? Работать? НИ-ФИ-ГА! Код должен быть быть читабельным! Чтоб любой человек смог его прочитать.

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

Соответственно (независимо от языка):

  1. однобуквенные переменные возможны только если они используются не более чем в 3..5 строках. В крайне редких случаях допускается количество строк увеличить до 10, дальше – х`генушки.
  2. любая переменная должна своим именем показывать, что в ней хранится. Слой? layer. Примитив чертежа? entity. Определение блока? Почему б не block_def / blockDef? Ну и тому подобное. То же самое касается и имен методов / функций: имя должно показывать, что эта хрень вообще делает. Имя в лиспе mxm вряд ли можно назвать информативным, а вот matrix-multiply-by-matrix уже подскажет самим своим именем, что выполняется умножение матрицы на матрицу. Точно так же GetBD и GetBlockDefinition немного сильно разнятся по своей информативности. Не стоит экономить на нажатиях клавиш!
  3. код повторяется больше одного раза? Выносить его в приватный метод (NET) / локальную функцию (lisp)
  4. код занимает больше экрана или выполняет вполне себе законченную логическую операцию? Выносить в приватный метод (NET) / локальную функцию (lisp)
  5. если код на шарпе, то приватные методы / поля / свойства скидываются в конец кода класса, а первым идут конструкторы класса. В лиспе придется локальные функции бросать вперед, иначе код тупо не заработает
  6. все документировать. Все что можно и что нельзя. Вспомнить, что творит какая-то функция, через полгода будет практически нереально
  7. стараться минимально привязываться к глобальным переменным. Любой метод / функция должен(на) работать только с тем, что ей передано
  8. если количество параметров вызова превышает 3 штуки, стоит подумать, как от такого стоит избавиться. А избавиться на самом деле не так уж и сложно ;) Передаем либо экземпляр класса (NET), либо список (lisp)

В качестве иллюстрации приведу кусок фейкового кода по нормализации блоков чертежа (не факт что текущего). Попробуйте оценить, насколько он читабелен. Код на лиспе:

1
2
3
4
5
6
7
8
9
10
;; Тупо для иллюстрации, поэтому переменные / функции / ... не локализую
(defun _kpblc-block-normalize (doc param-list)

  (setq block_def_list (fun_get-block-definitions (vla-get-blocks doc)))
  (setq layer_status (fun_unlock-thaw-layers (vla-get-layers doc)))
  (foreach block_def block_def_list
    (fun_normalize block_def param-list)
  )
  (fun_restore-layers doc layer_status)
)

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

При всем при этом есть еще одно правило в лиспе, которое я стараюсь соблюдать: любая локальная функция может быть вытащена наружу в любой момент и протестирована в отрыве от своего “владельца”.

Нейминг в C#, настройки VS / VS + Resharper
Понятно, что есть официальные требования от MS по именованиям. Но в процессе работы выяснилось, что лично мне такие правила не сильно подходят :( Так что я разработал свои собственные, которые и пользую.

Во-первых, до этих настроек надо добраться. В поле поиска вколачиваю “имен”:
2025-01-12_19-05-12
На старте получаю нечто типа:
2025-01-12_19-07-07
Дополняя этот список, можно, во-первых, указать, что именование может быть не рекомендацией, а критическим багом / ошибкой, а заодно указать правила именования всего чего угодно:
2025-01-12_19-10-12

К сожалению, сама VS “по умолчанию” предоставляет мало возможностей для контроля именований локальных переменных, переменных уровня класса и тому подобное. Но и такое уже лучше чем ничего. С другой стороны, кто мешает свои стили создать?

2025-01-12_19-17-06

2025-01-12_19-18-08

Поиграться можно, и, может быть, даже чего-то добиться )))

Теперь мои правила по именованиям переменных / констант / методов / классов / …

  1. Имена интерфейсов начинаются с символа “I” (заглавная “i”)
  2. Имена классов – PascalCase, т.е. начинаются с заглавной буквы. Значимые слова просто начинаются с заглавной буквы. К примеру, класс CurrentUser должен работать / показывать / возможно, менять что-то, что касается текущего пользователя. И ничего более
  3. Имена методов класса – тоже PascalCase. Т.е. CurrentUser.RegisteredLogin, CurrentUser.CanSaveToDbase и т.д.
  4. Имена параметров – опять же PascaleCase. Т.е. CurrentUser.CanLog(string LogFileName). Если что – в официальных рекомендациях идут camelCase. Но мне с таким работать показалось не очень удобным
  5. Имена локальных переменных внутри любого метода – camelCase. Т.е. string someValue = "abc" и тому подобное
  6. Имена локальных констант – UPPER_CASE. К примеру, internal const int ARMATURE_LENGTH_COLUMN = 15;
  7. Имена переменных уровня класса – _camelCase. Например, private int _armature_range;
  8. Перечисления могут в своем имени содержать слово Enum. А могут и не иметь. Но ошибкой это точно не является

Вроде ничего критического не упустил…

Кстати, об именах переменных ;) Лично я считаю ненормальным в имени переменной указывать ее тип. Т.е. sBlockName я заменю на blockName – имя уже показывает, что в ней болтается имя блока. Т.е. 100% строка. iRange преобразовывается в range, ну и так далее.

Подобное может иметь смысл в языках с динамической типизацией, но в шарпе?..

Ну ок, чистую VS разобрали плюс-минус, двигаюсь дальше.

Под VS есть масса дополнений, и одно из моих любимых – Resharper от JetBrains. Методы скачивания / установки / активации расписывать не буду, их и так тьма. Проблема в том, что после установки и активации этого расширения его настройки “перекрывают” настройки VS:
2025-01-12_19-26-10

И вот тут уже возможностей немного больше прям на старте:
2025-01-12_19-28-16

Соответственно, выполнив настройки и слегка поигравшись с собственным кодом, можно много всякого интересного узнать о себе и качестве именования :)

Нейминг в VLIDE / VSCode + AutoLISP
С лиспом ситуация слеганца похуже. Хотя б потому, что есть VLIDE (со своими настройками) и VSCode (где настроек мало того что слегка поменьше, так и реализованы они совсем по-другому!) Ну а тот факт, что ни одна из сред разработки не позволяет хоть как-то контролировать именование – и ситуация становится совсем печальной.

Лисповой код лично у меня редко бывает коротким и слабовложенным (хотя иногда так хочется все поменять!). Так что в VLIDE настройки у меня обычно такие:
2025-01-12_20-09-00
2025-01-12_20-10-20

Такие настройки позволяют форматировать код в плюс-минус читаемый вариант (сугубо ИМХО!)

Для VSCode + AutoLISP настройки расширения поставил такими (худо-бедно работают):
2025-01-12_20-13-50
2025-01-12_20-14-59

Ну и туда же: глобальные функции предваряются _kpblc. Символ подчеркивания означает, что это вообще функция, и она что-то возвращает. Дальше идет описание из слов на английском языке. Значимые слова разделяются символом “-“.

Т.е., например, функция конвертации чего-то в строку будет иметь имя _kpblc-conv-value-to-string. А преобразования чего-то в ename-указатель – _kpblc-conv-value-to-ename. Ну и тому подобное

Организация кода
Чутка побухчу. Все, что ниже – личное мнение.

По лиспу все достаточно просто: локальные функции вперед, независимо от порядка их вызова, дальше основной код.

В шарпе ситуация другая. Коснусь, пожалуй, только классов.

Есть конструктор класса? Отлично, помещаем все конструкторы сразу после объявления класса, на самом верху.

Публичные методы / свойства / поля? Сразу после конструктора.

Все приватное – в самый низ. Сначала пойдут приватные методы, потом уже приватные переменные, и в самом конце – приватные константы.

Вспомню про класс CurrentUser. Допустим, у него есть поля Login, Password, ConnectionString (для подключения к БД). В таком случае его код станет похож на нечто типа:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class CurrentUser
{
    public CurrentUser(string RegistryKey)
    {
        // Чтение данных из реестра и заполнение Login / Password / ...
        ConnectionString = EvaluateConnectionString();
    }

    public string Login
    {
        get => _login;
        private set => _login = value;
    }

    public string Password
    {
        get => _password;
        private set => _password = value;
    }

    public string ConnectionString
    {
        get => _connectionString;
        private set => _connectionString = value;
    }

    private string EvaluateConnectionString(string Login, string Password)
    {
        return
            $"Data Source=ServerLocation;Initial Catalog=DbaseName;Persist Security Info=True;User ID={Login};Password={Password}";
    }

    private string _login;
    private string _password;
    private string _connectionString;
}

Класс фейковый и к реальной жизни, понятное дело, не имеет никакого отношения. Но тут дело такое… Конструктор – в самом начале. Потом идут публичные поля / методы / свойства. И в самом конце – приватные методы и переменные. По моему опыту такой код читать и просматривать значительно проще и быстрее, чем любой другой вариант организации кода.

Ну и не стоит забывать про документирование. Реально нужно – даже если кажется, что код / название поля-метода-класса говорит само за себя.



Поделитесь своим мнением


Я не робот.