Привет! Меня зовут Очаковский Владимир. Сегодня хочу предложить тебе пройти практику на OneScript. Мы будем вместе разрабатывать собственную библиотеку — от самых первых шагов до полноценной реализации, включая версию на библиотеке ОСень.
Мы будем работать на основе заранее подготовленного технического задания, чтобы процесс разработки был максимально приближен к реальным условиям. Это поможет нам не только разобраться в ключевых принципах, но и научиться эффективно структурировать и выполнять задачи в рамках требований.
Хочу сразу сказать, что я не являюсь экспертом в этой области. Скорее, я такой же практик, как и ты. Наша цель — вместе пройти все этапы, разложить процесс по полочкам и на практике разобраться, как все это работает.
В конце мы получим две версии библиотеки: базовая и улучшенная на основе ОСени. А еще ты найдешь практические задания, чтобы попробовать свои силы, отточить навыки и, возможно, сделать нашу библиотеку еще лучше.
Так что, если ты готов — давай начинать!
Перед началом
Прежде чем мы начнем, хочу отметить, что наш мастер-класс рассчитан на тех, кто уже знаком с базовыми принципами OneScript. Мы не будем подробно разбирать основы (установка, настройка, отладка), чтобы сосредоточиться на практических аспектах разработки.
Чтобы ты смог легче погрузиться в материал и получить максимум пользы, я рекомендую ознакомиться с некоторыми полезными ресурсами заранее:
- Мастер-класс «OneScript. От простого к сложному с Никитой Иванченко» — отличный быстрый старт для понимания структуры и возможностей языка. В этом мастер-классе шаг за шагом создается простой проект, который затем преобразуется в реализацию на основе библиотеки ОСень. Это отличный способ увидеть весь процесс перехода от базовой идеи к более сложной реализации. Нас с тобой ждет что-то подобное!
- Статья «Создаем свою библиотеку для OneScript»— фундаментальная статья для создания своей библиотеки.
- Документация OneScript: добавь её в закладки и изучи основные разделы — она станет твоим незаменимым помощником в процессе работы.
Если ты уже знаком с этими материалами, то ты готов к нашему совместному погружению в практику. Если нет, рекомендую начать с них, чтобы уверенно двигаться вперед.
Техническое задание
Теперь тебе предстоит ознакомиться с техническим заданием, которое станет основой для разработки нашей библиотеки.
Цель: Создание библиотеки для мониторинга сервера.
Основные требования:
- Расширяемость:
- Модульная структура для легкого добавления новых методов.
- Один JSON-файл для хранения конфигурации всех методов.
- Функционал логирования:
- Запись событий (успехи, ошибки) с деталями в файл и вывод в консоль.
- Уведомления:
- Telegram и Email как основные виды транспорта.
- Легкость добавления новых видов транспорта.
Метод проверки дисков:
- Настройки: список дисков, пороговое значение (в процентах).
- Уведомления при превышении порога.
- Вывод информации в консоль (доступное и общее место, проблемные диски).
Расширение библиотеки (возможности):
- Мониторинг CPU и RAM.
- Проверка состояния служб и времени отклика сервисов.
- Проверка доступности внешних ресурсов.
Полное техническое задание доступно по ссылке.
Я подготовил общую диаграмму деятельности и простую диаграмму классов для методов проверки, чтобы облегчить проектирование начальной архитектуры библиотеки.
Диаграмма деятельности

Диаграмма классов

Для обеспечения гибкости и простоты добавления новых методов мониторинга мы будем использовать единый интерфейс для всех проверок. Это позволит легко добавлять новые проверки, не изменяя базовую логику.
На основе технического задания и предложенных диаграмм попробуй спроектировать начальную структуру библиотеки.
Исходя из наших требований, хотелось бы, чтобы приложение начинало работу следующим образом:
Монитор = Новый Монитор();
Монитор.ЗапуститьМониторинг();
Теперь мы готовы перейти к реализации!
- Структура проекта
- Файл конфигурации
- Проверка дисков
- Логирование
- Уведомления
- Сборка
- Консольный интерфейс
Версия Базовая
Структура проекта
Перед началом создания базовой версии нашей библиотеки, давай зафиксируем основные шаги и договоренности о её устройстве из документации:

Шаги по созданию структуры:
-
Создание корневого каталога проекта:
- Назовем корневой каталог
monitor.
- Назовем корневой каталог
-
Подготовка структуры папок:
- В корне создаем подпапки:
Классы— для размещения классов библиотеки.Модули— для реализации проверок и вспомогательных функций.
- В корне создаем подпапки:
-
Создание стартового файла:
- В корне проекта создаем файл
main.os, который станет точкой входа в нашу библиотеку.
- В корне проекта создаем файл
-
Создание ключевых компонентов:
- На основе диаграммы классов создаем:
- Класс
Монитор.osв папкеКлассы. - Модуль
ПроверкаДисков.osв папкеМодули.
- Класс
- На основе диаграммы классов создаем:
Промежуточная структура библиотеки:
- monitor
- Классы
- Монитор.os
- Модули
- ПроверкаДисков.os
- main.os
- Классы
Реализация начального каркаса логики:
В main.os добавляем стартовый код и инструкцию #Использовать “.” для загрузки наших классов и методов:
#Использовать "."
Монитор = Новый Монитор();
Монитор.ЗапуститьМониторинг();
Все проверки располагаются в папке Модули и следуют единому шаблону наименования Проверка<Назначение>.os. Также наши проверки имеют единый интерфейс. ПроверкаДисков.os:
Процедура ВыполнитьПроверку() Экспорт
Сообщить("Начало проверки дисков");
Сообщить("Завершение проверки дисков");
КонецПроцедуры
Создадим модуль Мониторинг.os для набора общих методов по управлению проверками:
#Область Проверки
Функция ДоступныеПроверки() Экспорт
Проверки = Новый Соответствие();
Проверки.Вставить("ПроверкаДисков", ПроверкаДисков);
Возврат Проверки;
КонецФункции
#КонецОбласти
Добавляем реализацию метода ЗапуститьМониторинг, который запускает все проверки в Монитор.os:
Процедура ЗапуститьМониторинг() Экспорт
Проверки = Мониторинг.ДоступныеПроверки();
Для Каждого Проверка Из Проверки Цикл
Менеджер = Проверка.Значение;
Менеджер.ВыполнитьПроверку();
КонецЦикла;
КонецПроцедуры
Тестирование:
- Создаем отладочный файл (launch.json).
- Запускаем приложение, проверяя корректность работы методов.

Теперь у нас есть базовая структура и начальный функционал библиотеки. Это станет отправной точкой для дальнейшего развития и улучшений!
Файл конфигурации
На основании технического задания и предложенных диаграмм попробуй самостоятельно представить, как должен выглядеть файл config.json. Сравни свою версию с моим описанием и подумай, насколько близки они по структуре и содержанию.
Для удобства пользователей нашей библиотеки мы также создадим файл-шаблон example_config.json, который будет находиться в корне проекта. Это позволит всегда иметь под рукой пример настроек, облегчающий начальную конфигурацию.
Файл конфигурации будет включать следующие параметры:
- Настройки логирования: путь к файлу логов.
- Список проверок, которые нужно выполнять, с параметрами для каждой проверки (например, список дисков и пороговые значения для проверки дискового пространства).
- Настройки уведомлений: данные для подключения к Telegram и/или электронной почте.
Пароли, токены и другие конфиденциальные данные не должны храниться в открытом виде в файлах конфигурации или в коде. В рамках мастер-класса мы упростили этот момент для демонстрации, но в реальных проектах обязательно учитывай это и используй безопасные подходы для хранения таких данных:
-
Секреты CI/CD
Используй встроенные механизмы управления секретами в системе CI/CD. -
Переменные окружения (Environment Variables)
Храни пароли и токены в переменных окружения на сервере или рабочем окружении. -
Локальные конфигурационные файлы, исключенные из контроля версий
Если ты вынужден использовать файлы, не забудь добавить его в.gitignore, чтобы он не попадал в репозиторий.
Перед тем как приступить к реализации работы с конфигурационным файлом, я советую тебе провести небольшое исследование. Это не только поможет лучше понять, как решать задачу, но и облегчит написание кода.
Попробуй следующий подход:
1. Проверка синтакс-помощника
Если ты еще не знаком с синтакс-помощником, самое время это исправить.
Например, для работы с JSON я нашел там метод ПрочитатьJSON. Этот метод считывает содержимое файла и преобразует его в структуры данных. Удобно и просто — то, что нужно! Вот ссылка на метод — обязательно посмотри.
2. Исследование готовых библиотек
На GitHub есть библиотека пакетов oscript-library. Часто там можно найти уже готовые инструменты.
Например, я нашел библиотеку configor. Она как раз создана для работы с конфигурационными файлами. Там уже есть методы для чтения JSON и управления настройками. Готовый интерфейс — бери и используй!
3. Анализ похожих решений в библиотеках
Еще один крутой способ — посмотреть, как похожие задачи решаются в других библиотеках. Например, я заглянул в популярный проект vanessa-runner.
В нем есть модуль ОбщиеМетоды.os, где нашел методы ПрочитатьНастройкиФайлJSON и ПрочитатьФайлJSON. Эти методы используют библиотеку json для работы с файлами. Хотя сейчас появился метод ПрочитатьJSON, этот пример все равно полезен: можно увидеть, как организована работа с настройками.
Что предлагаю сделать
На основе всего этого я бы предложил такой подход:
- Создать отдельный класс
Конфигурация.os, который будет изолировать логику работы с настройками. - Включить в класс публичные методы для чтения, проверки и получения значений настроек.
- Для работы с JSON используем метод
ПрочитатьJSON— он встроенный, простой и эффективный.
Такой способ даст нам гибкость, позволит легко менять логику и добавлять новые возможности.
ПриСозданииОбъекта
Когда мы создаем объект класса, у него есть предопределенный метод ПриСозданииОбъекта, который автоматически срабатывает при инициализации.
Конфигурация = Новый Конфигурация();
Это удобно, но заставляет задуматься: где лучше разместить логику чтения конфигурационного файла?
Есть два подхода: реализовать эту логику в методе ПриСозданииОбъекта или вынести в отдельный метод Инициализировать. Давай разберемся.
Плюсы:
- Простота использования. После создания объекта он сразу готов к работе, дополнительных вызовов не требуется.
- Единый путь инициализации. Это уменьшает вероятность ошибок, если разработчик забудет вызвать метод
Инициализировать.
Минусы:
- "Тяжелый" конструктор. С ростом функционала объект может выполнять слишком много задач сразу (например, настройку лога, проверку файла, чтение настроек). Это усложнит отладку.
- Сложности тестирования. Нельзя создать объект без выполнения всей логики, что может затруднить модульные тесты.
Плюсы:
- Простота конструктора. Весь тяжелый функционал вынесен, что делает код более читаемым и понятным.
- Удобство тестирования. Объект можно протестировать в разных состояниях — до и после инициализации.
- Гибкость. Метод можно вызывать с дополнительными параметрами, если потребуется адаптировать инициализацию.
Минусы:
- Необходимость помнить. Разработчик обязан вызывать метод
Инициализировать, иначе объект не будет готов к работе. - Немного больше кода. Каждый раз после создания объекта придется добавлять вызов метода.
Мы можем объединить преимущества обоих подходов.
- В методе
ПриСозданииОбъектадобавляем проверку наличия файла конфигурации. Если файла нет, сразу сообщаем об ошибке, создаем шаблонный файл или используем значения по умолчанию. - Логику чтения настроек из файла реализуем в методе
Инициализировать. Это позволит вызывать его позже, если потребуется.
Преимущества смешанного подхода:
- Раннее обнаружение критических ошибок. Например, если конфигурационный файл отсутствует, мы узнаем об этом на этапе создания объекта.
- Гибкость. Основная логика инициализации может быть вызвана позже, с учетом дополнительных параметров или подготовки.
- Удобство тестирования. Можно создавать объект без полной инициализации, что особенно полезно при модульном тестировании.
Конфигурация = Новый Конфигурация();
Конфигурация.Инициализировать();
Такой подход делает наш код понятным, удобным в использовании и масштабируемым. Что скажешь?
Перед тем как приступить к реализации, давай обсудим, как лучше связать объекты Конфигурация и Монитор. Здесь есть два варианта:
- Создавать экземпляр класса
КонфигурациявнутриМониторв методеПриСозданииОбъекта. - Передавать объект
КонфигурациявМониторв качестве параметра (через конструктор).
Я думаю, ты уже знаешь, какой вариант правильнее выбрать, но давай все равно их разберем.
Создание экземпляра класса вне конструктора и передача его в конструктор имеет несколько преимуществ по сравнению с созданием объекта непосредственно в конструкторе. Вот основные причины:
- Повторное использование. Один экземпляр
Конфигурацииможно передать нескольким объектамМониторили другим классам. - Гибкость. Легко подменить экземпляр
Конфигурациина другую версию, если это потребуется. - Тестируемость. Можно передать тестовый экземпляр или мок-объект, изолируя зависимости и делая тесты проще и предсказуемее.
- Прозрачность. Конструктор
Мониторявно показывает, что он зависит от объектаКонфигурация. - Следование SOLID. Класс
Мониторостается сфокусированным на своей задаче, а создание и настройка конфигурации передаются внешнему коду, реализуя принцип инверсии зависимостей (Dependency Injection) и упрощая управление зависимостями. Если ты не сталкивался с данными аббревиатурами ранее, то не забивай пока себе голову — мы еще столкнемся с Dependency Injection при переходе на ОСень.
Конфигурация = Новый Конфигурация();
Конфигурация.Инициализировать();
Монитор = Новый Монитор(Конфигурация);
Монитор.ЗапуститьМониторинг();
Мы готовы двигаться дальше!
Перед тем как перейти к реализации, давай визуализируем наши изменения на диаграмме классов. Это поможет нам четко понять, как взаимодействуют объекты Конфигурация и Монитор, и убедиться, что структура соответствует нашим целям.

Обрати внимание, что у класса Конфигурация появился метод Проверки(), который возвращает загруженные проверки из конфигурационного файла.
Кроме того, у методов ВыполнитьПроверку(ПараметрыПроверки) теперь появился параметр ПараметрыПроверки. Этот параметр будет содержать настройки конкретной проверки, которые передаются из метода ПараметрыПроверки.
Давай переходить к реализации!
Начнем с файла main.os:
#Использовать "."
Конфигурация = Новый Конфигурация();
Конфигурация.Инициализировать();
Монитор = Новый Монитор(Конфигурация);
Монитор.ЗапуститьМониторинг();
Добавим параметр ПараметрыПроверки в ПроверкаДисков.os:
Процедура ВыполнитьПроверку(ПараметрыПроверки) Экспорт
Сообщить("Начало проверки дисков");
Сообщить("Завершение проверки дисков");
КонецПроцедуры
Создадим класс Конфигурация.os:
Перем Настройки;
Процедура ПриСозданииОбъекта()
ИмяФайла = ИмяФайлаПоУмолчанию();
ФайлКонфигурации = Новый Файл(ИмяФайла);
Если Не ФайлКонфигурации.Существует() Тогда
ВызватьИсключение "Не найден файл конфигурации: " + ИмяФайла;
КонецЕсли;
КонецПроцедуры
#Область ПрограммныйИнтерфейс
Процедура Инициализировать() Экспорт
Настройки = ПрочитатьФайлJSON(ИмяФайлаПоУмолчанию());
КонецПроцедуры
Функция Проверки() Экспорт
Возврат Настройки["Проверки"];
КонецФункции
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
Функция ИмяФайлаПоУмолчанию()
Возврат "config.json";
КонецФункции
Функция ПрочитатьФайлJSON(ИмяФайла)
Сообщить("Читаю настройки из " + ИмяФайла);
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.ОткрытьФайл(ИмяФайла);
Результат = ПрочитатьJSON(ЧтениеJSON, Истина);
ЧтениеJSON.Закрыть();
Возврат Результат;
КонецФункции
#КонецОбласти
Доработаем класс Монитор.os для работы с конфигурацией:
Перем _Конфигурация;
Процедура ПриСозданииОбъекта(Конфигурация)
_Конфигурация = Конфигурация;
КонецПроцедуры
Процедура ЗапуститьМониторинг() Экспорт
ЗагруженныеПроверки = _Конфигурация.Проверки();
ДоступныеПроверки = Мониторинг.ДоступныеПроверки();
Для Каждого Проверка Из ЗагруженныеПроверки Цикл
ИмяПроверки = Проверка.Ключ;
ПараметрыПроверки = Проверка.Значение;
Менеджер = ДоступныеПроверки.Получить(ИмяПроверки);
Если Менеджер = Неопределено Тогда
Сообщить("Не найден менеджер для проверки: " + ИмяПроверки);
Продолжить;
КонецЕсли;
Если Не ПараметрыПроверки["Использовать"] Тогда
Сообщить("Отключена проверка: " + ИмяПроверки);
Продолжить;
КонецЕсли;
Менеджер.ВыполнитьПроверку(ПараметрыПроверки);
КонецЦикла;
КонецПроцедуры
Теперь цикл обработки проверок работает не по всем доступным проверкам, а только по загруженным из конфигурации. Дополнительно мы проверяем, используется ли конкретная проверка, прежде чем вызывать ее выполнение. Это позволяет избежать лишних вызовов и обеспечивает гибкость в управлении настройками.
Тестирование:
- В отладке убедись, что параметры передаются корректно в метод выполнения проверки:

- Проверь корректность работы приложения в целом:

Не забудь проверить различные сценарии, например, когда нету файла конфигурации или проверка отключена.
Текущая структура библиотеки:
- monitor
- Классы
- Конфигурация.os
- Монитор.os
- Модули
- Мониторинг.os
- ПроверкаДисков.os
- main.os
- example_config.json
- Классы
Проверка дисков
Для реализации нашей первой проверки дисков мы будем работать с методом ИнформацияОДиске из синтакс-помощника. Этот метод предоставляет информацию о доступном пространстве и общем размере диска, что идеально подходит для нашей задачи.

Попробуй самостоятельно реализовать логику проверки дисков, используя этот метод.
Процедура ВыполнитьПроверку(ПараметрыПроверки) Экспорт
Сообщить("Начало проверки дисков");
Диски = ПараметрыПроверки["Диски"];
Порог = ПараметрыПроверки["Порог"];
Для Каждого Диск Из Диски Цикл
ПроверитьДиск(Диск, Порог);
КонецЦикла;
Сообщить("Завершение проверки дисков");
КонецПроцедуры
Основная идея проверки:
- Получить данные о дисках.
- Рассчитать процент свободного места.
- Вывести сообщение с информацией согласно техническому заданию.
Когда закончишь свою реализацию, обязательно сравни её с моей версией.
Тестирование:
- В config.json указываем настройки для проверки ПроверкаДисков:

- Запускаем приложение, проверяя корректность работы методов:

Логирование
Для того чтобы закрыть функциональные требования из технического задания, нам понадобится механизм логирования. Я предлагаю использовать готовую библиотеку logos. Она поддерживает параллельный вывод логов в несколько источников, что идеально подходит для нашего проекта.
Так как это первая внешняя библиотека в нашем проекте, начнём с её установки. Выполни команду для установки:
opm i logos
Для структурированного логирования мы будем использовать основной журнал с именем нашего приложения. Давай начнем с создания модуля ПараметрыПриложения.os. В нём добавим функцию, которая возвращает имя приложения:
Функция ИмяПриложения() Экспорт
Возврат "monitor";
КонецФункции
Теперь это имя станет основой для нашего журнала логов.
Поскольку модуль Мониторинг является основным для нашего приложения, создание и настройку логов разместим именно там, чтобы она была в одном месте. Особенностью модулей в OneScript является то, что у них есть переменные и они сохраняют значение после последнего присвоения. Это удобно для инициализации единого экземпляра объекта лога. Подключим в модуле библиотеку и создадим переменную для лога:
#Использовать logos
Перем Лог;
Теперь создадим функцию, которая будет возвращать сам лог:
Функция Лог() Экспорт
Если Лог = Неопределено Тогда
Лог = Логирование.ПолучитьЛог(ПараметрыПриложения.ИмяПриложения());
КонецЕсли;
Возврат Лог;
КонецФункции
Теперь давай найдем все строки, где используется Сообщить

И заменим их на методы библиотеки logos. Вот основные методы:
- Лог.Информация("Информация");
- Лог.Ошибка("Ошибка");
- Лог.Отладка("Отладка");
- Лог.КритичнаяОшибка("Критичная ошибка");
Лог = Мониторинг.Лог();
Лог.Информация("Информация");
Для того чтобы включить отладочные логи в приложении, рекомендую изучить документацию библиотеки logos. Это поможет понять все доступные возможности настройки.
Один из вариантов — использование конфигурационного файла logos.cfg. В этом файле можно задать уровень логирования для нашего приложения. Например:
logger.monitor=DEBUG
Задание
- Подключи библиотеку logos в тех модулях, где используется
Сообщить. - Замени вызовы
Сообщитьна соответствующие методы библиотеки. - Проверь, что лог корректно работает, и сообщения выводятся в указанный журнал.
После выполнения задания сравни свой результат с моей версией.

Давай научимся настраивать форматирование сообщений в логах. Это позволит сделать вывод более удобным и информативным. Выполним нужные шаги по установке раскладки по документации в модуле Мониторинг.os:
Функция Лог() Экспорт
Если Лог = Неопределено Тогда
Лог = Логирование.ПолучитьЛог(ПараметрыПриложения.ИмяПриложения());
Лог.УстановитьРаскладку(ЭтотОбъект);
КонецЕсли;
Возврат Лог;
КонецФункции
#Область СлужебныйПрограммныйИнтерфейс
Функция ПолучитьФорматированноеСообщение(Знач СобытиеЛога) Экспорт
Уровень = УровниЛога.НаименованиеУровня(СобытиеЛога.ПолучитьУровень());
ФорматированноеСообщение = СтрШаблон("%1: %2 - %3",
ТекущаяДата(),
Уровень,
СобытиеЛога.ПолучитьСообщение());
Возврат ФорматированноеСообщение;
КонецФункции
#КонецОбласти

Для записи логов в файл нужно донастроить наш лог. Так как имя файла лога указывается в конфигурационном файле, настройку вывода в файл мы выполним после чтения настроек. При этом важно учесть особенность библиотеки logos: если мы добавляем второй способ вывода (например, в файл), то первый способ (например, вывод в консоль) нужно явно указать, иначе он пропадет.
Изменения в Мониторинг.os:
#Область Логирование
Функция Лог() Экспорт
Если Лог = Неопределено Тогда
Лог = Логирование.ПолучитьЛог(ПараметрыПриложения.ИмяПриложения());
Лог.УстановитьРаскладку(ЭтотОбъект);
ВыводЛогаВКонсоль = Новый ВыводЛогаВКонсоль;
Лог.ДобавитьСпособВывода(ВыводЛогаВКонсоль);
КонецЕсли;
Возврат Лог;
КонецФункции
Процедура ДобавитьВыводЛогаВФайл(ФайлЛога) Экспорт
Лог = Лог();
ВыводЛогаВФайл = Новый ВыводЛогаВФайл;
ВыводЛогаВФайл.ОткрытьФайл(ФайлЛога, , Ложь);
Лог.ДобавитьСпособВывода(ВыводЛогаВФайл);
КонецПроцедуры
#КонецОбласти
Изменения в Конфигурация.os:
Процедура Инициализировать() Экспорт
Настройки = ПрочитатьФайлJSON(ИмяФайлаПоУмолчанию());
Мониторинг.ДобавитьВыводЛогаВФайл(ФайлЛога());
КонецПроцедуры
Функция ФайлЛога() Экспорт
Возврат Настройки["ФайлЛога"];
КонецФункции
После настройки перезапусти приложение, чтобы убедиться, что логи корректно выводятся в консоль и записываются в файл, указанного в конфигурации.

Кстати, вот пример настройки вывода лога через файл logos.cfg:
logger.monitor=DEBUG, result, console
appender.result=ВыводЛогаВФайл
appender.result.file=logs.txt
appender.console=ВыводЛогаВКонсоль
Поскольку в проекте появился модуль ПараметрыПриложения.os, перед завершением работы над логированием предлагаю сделать небольшой рефакторинг в Конфигурация.os. Это улучшит структуру кода и сделает его более модульным.
Вот что мы сделаем:
- Имя файла конфигурации перенесем в модуль ПараметрыПриложения:
- В ПараметрыПриложения.os создадим функцию
ИмяФайлаКонфигурации().
- В ПараметрыПриложения.os создадим функцию
- Создадим модуль РаботаСФайлами.os для операций с файлами:
- Вынесем всю логику, связанную с файлами, включая проверку их существования и открытие, в отдельный модуль.
Попробуй выполнить эти шаги самостоятельно, а затем сравни свой результат с моими изменениями.
Текущая структура библиотеки:
- monitor
- Классы
- Конфигурация.os
- Монитор.os
- Модули
- Мониторинг.os
- ПараметрыПриложения.os
- ПроверкаДисков.os
- РаботаСФайлами.os
- main.os
- example_config.json
- Классы
Уведомления
Мы дошли до реализации последнего крупного функционального блока — уведомления. Этот компонент позволит нашему приложению сообщать о результатах проверок через различные каналы связи.
Проектирование функционала
Сначала нужно продумать архитектуру. Обратимся к файлу config.json. В нем уже есть ключевая информация:
- Имя транспорта — название канала связи (например, почта или телеграм).
- Настройки транспорта — данные для подключения и настройки канала.
Также нам не обойтись без параметров уведомления - структура, включающая заголовок, тело сообщения, вложения и дополнительные параметры, которые будут использоваться при отправке.
На основании технического задания нам нужно:
- Реализовать отправку уведомлений через почту и телеграм.
- Обеспечить возможность легкого добавления новых транспортов.
Для этого за основу возьмем подход, схожий с архитектурой проверок, но адаптируем его:
- Все транспорты будут представлены модулями.
- Создание отдельного класса для каждого транспорта избыточно, так как мы лишь вызываем конкретную реализацию функции отправки, а дополнительные состояния и сложная логика в данном случае отсутствуют.
Для наглядности представим архитектуру функционала в виде диаграммы классов:

Уведомления— основной интерфейс, управляющий транспортами и уведомлениями.- Метод
ОтправитьУведомлениеопределяет общую логику отправки. МенеджерТранспортаотвечает за подключение конкретного модуля транспорта.
- Метод
УведомленияПочтаиУведомленияТелеграм— модули, реализующие свои методы отправки.Уведомления<НовыйТранспорт>— пример для добавления нового транспорта. Достаточно создать новый модуль и реализовать в нем методОтправитьУведомление.
Будет достаточно создать новый модуль, реализующий метод отправки, без изменений основной логики и каждая реализация изолирована и легко заменяема.
Теперь, когда мы спроектировали начальную архитектуру, давай приступим к реализации. 🚀
Начальная реализация
По диаграмме классов создадим каркас методов:
Уведомления.os
#Область ПрограмммныйИнтерфейс
Процедура ОтправитьУведомление(ИмяТранспорта, НастройкиТранспорта, ПараметрыУведомления) Экспорт
КонецПроцедуры
Функция ПараметрыУведомления() Экспорт
Параметры = Новый Соответствие;
Параметры.Вставить("Тема");
Параметры.Вставить("Текст");
Параметры.Вставить("Вложение");
Возврат Параметры;
КонецФункции
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
Функция МенеджерТранспорта(ИмяТранспорта)
Менеджеры = Новый Соответствие;
Менеджеры.Вставить("Почта", УведомленияПочта);
Менеджеры.Вставить("Телеграм", УведомленияТелеграм);
Возврат Менеджеры[ИмяТранспорта];
КонецФункции
#КонецОбласти
УведомленияПочта.os
Процедура ОтправитьУведомление(НастройкиТранспорта, ПараметрыУведомления) Экспорт
Сообщить("Отправка уведомления через почту");
КонецПроцедуры
УведомленияТелеграм.os
Процедура ОтправитьУведомление(НастройкиТранспорта, ПараметрыУведомления) Экспорт
Сообщить("Отправка уведомления через телеграм");
КонецПроцедуры
Итак, нам нужно определить, где именно в нашем коде будет происходить вызов отправки уведомлений. На первый взгляд, логично разместить эту логику в методе ЗапуститьМониторинг, который уже отвечает за выполнение всех проверок и координацию работы приложения.
Монитор.ЗапуститьМониторинг();
Однако стоит задуматься, соответствует ли это подход принципу единой ответственности (SRP, Single Responsibility Principle). Если мы добавим логику отправки уведомлений в ЗапуститьМониторинг, этот метод станет перегруженным и сложным для тестирования. Кроме того, это создаст дополнительную связанность кода, что ухудшит его поддержку и расширение.
Чтобы избежать этих проблем, правильнее бу
Вступайте в нашу телеграмм-группу Инфостарт
