Рекомендации по декомпозиции методов и их расположению

3 0

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

 

Зачем разбивать функции

Можно выделить ситуации, говорящие о том, что стоит вынести часть кода в отдельную функцию.

  1. В коде встречается повторяющийся фрагмент. Каждый раз, когда в программе встречается повторяющийся фрагмент кода, он указывает на упущенную возможность для абстракции. Устранение дублирования через создание новых функций обеспечивает возможность переиспользования уже готовой логики другими элементами разработки. Также снижается трудоёмкость сопровождения кода, так как используемая во многих местах логика, может быть доработана лишь единожды.
  2. Код выглядит сложным и неочевидным. Простота – одна из самых главных характеристик хорошего кода. Простой код легко понять – он последователен и прямолинеен, не содержит сложных неочевидных вызовов и запутанной обработки ошибок. Также он не берёт на себя лишней ответственности, делая только то что от него ждут. Выделяя и разбивая сложные конструкции в отдельные функции, можно значительно повысить читаемость кода.

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

  4. Код занимает более 1-2 экранов монитора. Удобнее всего в один момент времени видеть весь необходимый код перед глазами. Перевести взгляд от одной части статичного изображения к другой гораздо проще, чем прокручивать страницу, теряя из-за этого ранее анализируемую строчку кода. При разрастании функции на несколько экранов монитора, анализ и сопровождение становится более трудоёмким в следствие усложнения читаемости. Рекомендуется избегать функций больше 2 экранов (примерно 90 строк). Большинство функций, в идеале, должно быть меньше 1 экрана (примерно 45 строк).

 

Где создавать функции

При выборе места размещения функции рекомендуется задавать себе вопрос: откуда вызывать эту функцию будет логичнее всего? Определите контекст, в котором функция смотрелась бы наиболее правильно с точки зрения объектной модели и где разработчик стал бы искать её в первую очередь.

Функции решения

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

Функции модуля

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

Примеры: конвертация документов в PDF; функции интеграции; действия обложки модуля; общие вычисляемые выражения.

Функции типа сущности

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

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

Инициализация

Функции инициализации создаются исключительно для инициализации модуля, в котором они определены.

Примеры: создание ролей; создание сущностей; выдача прав для ролей.

 

Какие создавать функции: клиентские, серверные или разделяемые

Информация о видах кода есть в справке.

Клиентские функции

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

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

Код используется для:

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

Примеры: показ диалога; валидация с выводом ошибки.

Серверные функции

Выполняются на сервере.

Так как серверный код выполняется на сервере, в нем не должно быть логики, которая связана с пользовательским интерфейсом (GUI). Это означает, что в серверном коде запрещено показывать окна, выводить диалоги.

Код используется для:

  • изменения сущности;
  • создания, получения и удаления сущности;
  • выполнения SQL-запросов;
  • работы с ZIP-архивами;
  • работы с транзакциями;
  • написания интеграционных функций;
  • написания функций вычисляемых выражений.

Примеры: удаление документа; выполнение запроса в БД; функция интеграции.

Разделяемые функции

Выполняются в любом коде.

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

Используется для:

  • изменения состояния и свойств сущности;
  • вычисления значений на основе свойств или расчёта по формулам.

Примеры: валидация сущности; заполнение свойств по алгоритму; установка обязательности свойств.

 

Какое дать название и описание функции

Вот основные рекомендации.

  • В имени используйте нотацию PascalCasing, для этого пишите первую букву каждого слова в верхнем регистре.

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

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

  • Сокращения не должны влиять на понятность имени. Не используйте аббревиатуры или неполные слова, если только они не являются общепринятыми.

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

  • Документируйте функции в XML-формате. Чтобы быстро сформировать описание по формату, в среде разработки над определением функции введите ///. В результате автоматически вставится структура комментария.
    Обязательно заполняйте описание функции (<summary>), параметров (<param>), выходного значения (<returns>), а также, при необходимости, примечание (<remarks>). В долгосрочной перспективе эти действия принесут немало пользы другим разработчикам (и вам в том числе).

Также информацию о правилах именования и документирования можно найти в справке.

 

Как поддерживать порядок в коде

Регионы

Внутри кода рекомендуется использовать регионы (#region) для объединения логически связанных функций или обработчиков.

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

Инициализация модуля

При инициализации происходит множество разнообразных действий, таких как:

  • заполнение справочников;
  • создание предопределенных групп пользователей и ролей;
  • выдача необходимых прав на объекты системы.

Для создания понятной структуры функций инициализации рекомендуется группировать их в логические регионы. Рекомендуется использовать в качестве шаблона структуру инициализации модуля Sungero.Docflow.

Функции решений, модулей и сущностей

Любой элемент системы может содержать в себе огромное количество логики, а значит и функций. Чтобы было проще ориентироваться в коде, рекомендуется объединять связанные по назначению функции в регионы.

Блоки кода внутри функций.

Напомню, что большие функции следует разбивать на более простые. Но если всё таки нет такой возможности "здесь и сейчас", а минимальный порядок и читаемость обеспечить важно - можно воспользоваться тем же регионом для разделения разнородных вычислений внутри функции. Это позволит сворачивать массивные блоки кода, которые в текущий момент не требуют внимания разработчика, а только мешают процессу анализа или разработки. Но будьте осторожны - в свёрнутом регионе могут "прятаться" важные вычисления, которые влияют на код ниже.

События блоков в схеме задач (без No-code)

При разработке новых типов задач может быть проблематично ориентироваться в коде их блоков. Для упрощения работы с кодом рекомендуется группировать события в регионы соответствующих блоков.

Группировка в разные файлы (25.2+)

Если код модуля или сущности содержит множество функций, связанных с разными механизмами, то можно воспользоваться группировкой кода в разные файлы.

Этот способ позволит облегчить и ускорить навигацию по следующим причинам:

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

Подробную информацию о группировке, а также пример можно найти в справке.

Пока комментариев нет.

Авторизуйтесь, чтобы написать комментарий