Лингво-тренер | Обновления
Теперь бот сохраняет историю переписки и отвечает в контексте вашего общения.
Ранее он терял нить повествования.
Проблема оказалось в том, что передавать уникальный ключ чата было недостаточно, чтобы бот помнил историю. Пришлось отправлять ему последние 5 сообщений переписки. Это увеличивает кол-во токенов, но зато дает релевантный ответ. Ответы стали короче и соответствуют обычному разговору, а не запросы в справочник.
Пробуйте @TalkSharp_bot
Теперь бот сохраняет историю переписки и отвечает в контексте вашего общения.
Ранее он терял нить повествования.
Проблема оказалось в том, что передавать уникальный ключ чата было недостаточно, чтобы бот помнил историю. Пришлось отправлять ему последние 5 сообщений переписки. Это увеличивает кол-во токенов, но зато дает релевантный ответ. Ответы стали короче и соответствуют обычному разговору, а не запросы в справочник.
Пробуйте @TalkSharp_bot
🔥6
Генерация речи в текст
В моем проекте @TalkSharp_bot изначально я использовал gTTS библиотеку для генерации речи.
Для первый версии этого достаточно.
Но как пользователю мне не нравится такая речь. Хотелось чтобы я могу выбрать голос собеседника.
Плюс gTTS довольно медленно работает. А для бота, который предполагает активное общение это критично.
Я посмотрел несколько решений и выбрал https://rhasspy.github.io/piper-samples/
Потрясающая библиотека.
Добавил ее в бота. Теперь приятно случать и фразы и просто истории, которые создает бот. Далее добавлю выбор акцента.
И конечно главное, скорость стала заметно быстрее.
Пробуйте @TalkSharp_bot
В моем проекте @TalkSharp_bot изначально я использовал gTTS библиотеку для генерации речи.
Для первый версии этого достаточно.
Но как пользователю мне не нравится такая речь. Хотелось чтобы я могу выбрать голос собеседника.
Плюс gTTS довольно медленно работает. А для бота, который предполагает активное общение это критично.
Я посмотрел несколько решений и выбрал https://rhasspy.github.io/piper-samples/
Потрясающая библиотека.
Добавил ее в бота. Теперь приятно случать и фразы и просто истории, которые создает бот. Далее добавлю выбор акцента.
И конечно главное, скорость стала заметно быстрее.
Пробуйте @TalkSharp_bot
🔥7
Решение о рефакторинге метода на 267 строк
В этом посте я показал одно из своих решений в проекте @TalkSharp_bot.
https://t.me/java_kotlin_hub/137
На самом деле таких мест в проекте несколько. Вот ещё одно.
В конце поста я попросил предложить, как можно избавиться от этого длинного хвоста.
Вас, конечно, уже не запутать — вы сразу предложили правильное решение.
https://github.com/peterarsentev/code_quality_principles?tab=readme-ov-file#2-dispatch-pattern-instead-of-multiple-if-statements-and-switch-anti-pattern
Это так называемый шаблон диспетчер.
Давайте на примере кода выше я объясню, как его можно было применить.
В коде выше есть один неудобный нюанс: метод execute – это не метод одного класса, а метод перегруженый в 100500 классах.
То есть мы не можем описать интерфейс
поскольку нет однородного объекта для передачи.
В этом случае нам нужно явно передать сервис в параметр, чтобы обойти эту проблему.
Теперь перепишем метод вызова.
Класс ContentRoute, который аккумулирует обработчики.
Но это ещё не всё. Есть сценарий, который должен выполниться, если мы не нашли подходящего потребителя. В данном случае, если объект Content не содержит ничего, кроме текста, то нужно отправить только текст.
Важно отметить, что мы не можем просто проверить
поскольку текст отправляется в других сценариях, например, в случае отправки кнопок.
Опять же, оставляю этот пост с открытым вопросом: как в этом случае поправить код?
В этом посте я показал одно из своих решений в проекте @TalkSharp_bot.
https://t.me/java_kotlin_hub/137
На самом деле таких мест в проекте несколько. Вот ещё одно.
public Optional<Integer> sent(Content content) {
try {
if (content.getDeleteMessageId() != null) {
execute(
new DeleteMessage(
content.getChatId().toString(),
content.getDeleteMessageId())
);
return Optional.empty();
} else if (content.getPinMessageId() != null) {
var message = new PinChatMessage();
message.setChatId(content.getChatId());
message.setMessageId(content.getPinMessageId());
message.setDisableNotification(true);
execute(message);
return Optional.of(message.getMessageId());
} else if (content.getUnpinMessageId() != null) {
var message = new UnpinChatMessage();
message.setChatId(content.getChatId());
message.setMessageId(content.getUpdateMessageId());
execute(message);
return Optional.of(message.getMessageId());
} else if (content.getMenu() != null) {
SendMessage message = new SendMessage();
message.setChatId(content.getChatId());
message.setText(md5Corrector.correctPairSymbols((content.getText())));
message.setParseMode("MarkdownV2");
var markup = new ReplyKeyboardMarkup();
markup.setKeyboard(content.getMenu());
markup.setResizeKeyboard(true);
message.setReplyMarkup(markup);
return Optional.of(execute(message).getMessageId());
}В конце поста я попросил предложить, как можно избавиться от этого длинного хвоста.
Вас, конечно, уже не запутать — вы сразу предложили правильное решение.
https://github.com/peterarsentev/code_quality_principles?tab=readme-ov-file#2-dispatch-pattern-instead-of-multiple-if-statements-and-switch-anti-pattern
Это так называемый шаблон диспетчер.
Давайте на примере кода выше я объясню, как его можно было применить.
В коде выше есть один неудобный нюанс: метод execute – это не метод одного класса, а метод перегруженый в 100500 классах.
То есть мы не можем описать интерфейс
interface Route {
boolean check(Content content);
Message process(Content content);
}поскольку нет однородного объекта для передачи.
В этом случае нам нужно явно передать сервис в параметр, чтобы обойти эту проблему.
public interface ContentType {
boolean check(Content content);
Optional<Integer> process(ReceiveUpdateService sender, Content content)
throws TelegramApiException;
}Теперь перепишем метод вызова.
public Optional<Integer> sent(Content content) {
try {
return contentRoute.process(this, content);
} catch (TelegramApiException e) {
log.error("The error occurred sending a message", e);
}
return Optional.empty();
}Класс ContentRoute, который аккумулирует обработчики.
@Service
@AllArgsConstructor
public class ContentRoute {
private final List<ContentType> contentType;
public Optional<Integer> process(ReceiveUpdateService sender, Content content)
throws TelegramApiException {
for (var type : contentType) {
if (type.check(content)) {
return type.process(sender, content);
}
}
return Optional.empty();
}
}
Но это ещё не всё. Есть сценарий, который должен выполниться, если мы не нашли подходящего потребителя. В данном случае, если объект Content не содержит ничего, кроме текста, то нужно отправить только текст.
Важно отметить, что мы не можем просто проверить
content.text != null
поскольку текст отправляется в других сценариях, например, в случае отправки кнопок.
Опять же, оставляю этот пост с открытым вопросом: как в этом случае поправить код?
👍4
Обработчик отправки только текста.
Хотите прокачаться в теме ООП, Spring Core и написать своего телеграм-бота?
Заходите сюда: https://job4j.ru/profile/courses/34
public class TextContent implements ContentType {
private final MD5Corrector md5Corrector;
@Override
public boolean check(Content content) {
return true;
}
@Override
public Optional<Integer> process(ReceiveUpdateService sender, Content content)
throws TelegramApiException {
var message = new SendMessage();Хотите прокачаться в теме ООП, Spring Core и написать своего телеграм-бота?
Заходите сюда: https://job4j.ru/profile/courses/34
Telegram
О жизни в IT | Java | Kotlin | Карьера | Job4j.ru
Класс с одним методом на 267 строк | ООП
Сегодня еще хотел бы показать вам один пример код из проекта IT-Тренер
В проекте есть класс, который отрабатывает события нажатия кнопок. Каждая кнопка имеет объект data. В зависимости от data происходит необходимо…
Сегодня еще хотел бы показать вам один пример код из проекта IT-Тренер
В проекте есть класс, который отрабатывает события нажатия кнопок. Каждая кнопка имеет объект data. В зависимости от data происходит необходимо…
👍8
ITTalkBot | Обновления
1. Поправил запросы, теперь отвечает четко по теме.
2. Вернул возможность отвечать текстом.
Пробуйте @ITTalkBot
1. Поправил запросы, теперь отвечает четко по теме.
2. Вернул возможность отвечать текстом.
Пробуйте @ITTalkBot
🤣2🔥1😁1
Системный дизайн | Хранение пользовательских настроек
После завершения блока DevOps напомню, что его ещё можно приобрести со скидкой за 7990 рублей (https://job4j.ru/profile/courses/35).
Я планирую выбрать в качестве самой сложной темы системный дизайн.
В целом задачи системного дизайна представляют собой совокупность ООП, шаблонов проектирования, проектирования баз данных и архитектурных решений.
То есть однозначных решений здесь не может быть и не должно быть.
Это напоминает мне аналогию с решением уравнений с неизвестными параметрами, где всегда можно добавить новое условие.
Здесь то же самое: этот блок является самым сложным на собеседовании, потому что к нему нельзя подготовиться искусственно, как, например, к алгоритмической секции.
Однако можно рассмотреть ряд популярных задач и на их основе вести рассуждения.
В сегодняшней статье я хотел предложить вам решение задачи по проектированию хранения пользовательских настроек.
Проблема появилась в моём проекте (https://t.me/talksharp_bot).
В первоначальной версии каждая настройка имела только одно значение, например: изучаемый язык, язык интерфейса, скорость речи и т.д.
После подключения языковой модели (https://github.com/rhasspy/piper) пользователь сможет выбирать голос для озвучивания сообщений, но для каждого языка необходимо указывать свою модель.
И тут появляется проблема: теперь мне нужно хранить для каждого языка отдельную настройку.
Изначально настройки ассоциируются с ключом
Постановка задачи.
Предложите несколько решений организации хранения пользовательских настроек. Оцените их с точки зрения расширяемости и гибкости использования.
Решения пишите в комментариях.
После завершения блока DevOps напомню, что его ещё можно приобрести со скидкой за 7990 рублей (https://job4j.ru/profile/courses/35).
Я планирую выбрать в качестве самой сложной темы системный дизайн.
В целом задачи системного дизайна представляют собой совокупность ООП, шаблонов проектирования, проектирования баз данных и архитектурных решений.
То есть однозначных решений здесь не может быть и не должно быть.
Это напоминает мне аналогию с решением уравнений с неизвестными параметрами, где всегда можно добавить новое условие.
Здесь то же самое: этот блок является самым сложным на собеседовании, потому что к нему нельзя подготовиться искусственно, как, например, к алгоритмической секции.
Однако можно рассмотреть ряд популярных задач и на их основе вести рассуждения.
В сегодняшней статье я хотел предложить вам решение задачи по проектированию хранения пользовательских настроек.
Проблема появилась в моём проекте (https://t.me/talksharp_bot).
В первоначальной версии каждая настройка имела только одно значение, например: изучаемый язык, язык интерфейса, скорость речи и т.д.
После подключения языковой модели (https://github.com/rhasspy/piper) пользователь сможет выбирать голос для озвучивания сообщений, но для каждого языка необходимо указывать свою модель.
И тут появляется проблема: теперь мне нужно хранить для каждого языка отдельную настройку.
Изначально настройки ассоциируются с ключом
public enum UserConfigKey {
TARGET_LANG(0),
LEVEL_LANG(1),
TOPIC_ID(2),
QUESTION_ID(3);
public final int key;
UserConfigKey(int key) {
this.key = key;
}
}Постановка задачи.
Предложите несколько решений организации хранения пользовательских настроек. Оцените их с точки зрения расширяемости и гибкости использования.
Решения пишите в комментариях.
Telegram
TalkSharp | Лингво-тренер
TalkSharp — тренируйте разговорные навыки через голосовые сообщения на английском, немецком и других языках! 🎙
🔥1
Тренды IT
Перенёс функции мониторинга рынка с CheckDev на Job4J. Это единственное, что осталось от проекта CheckDev.
Планирую добавить графики и общий индекс.
По трендам можно сказать, что лидеры — это JS, Java, C#, PHP, ML и Rust.
Не пишу здесь про Python, так как он требует сочетания с ML.
В целом, по общему индексу, рынок падает.
Я связываю это с ситуацией с СВО, которая оказывает огромное влияние на все сферы.
Замусолённую тему о замене программистов ИИ считаю не актуальной, так как все уже поняли, что без компетентного оператора ИИ ничего не сможет сделать. То есть падение рынка не связано с широким использованием ИИ.
https://job4j.ru/profile/
Перенёс функции мониторинга рынка с CheckDev на Job4J. Это единственное, что осталось от проекта CheckDev.
Планирую добавить графики и общий индекс.
По трендам можно сказать, что лидеры — это JS, Java, C#, PHP, ML и Rust.
Не пишу здесь про Python, так как он требует сочетания с ML.
В целом, по общему индексу, рынок падает.
Я связываю это с ситуацией с СВО, которая оказывает огромное влияние на все сферы.
Замусолённую тему о замене программистов ИИ считаю не актуальной, так как все уже поняли, что без компетентного оператора ИИ ничего не сможет сделать. То есть падение рынка не связано с широким использованием ИИ.
https://job4j.ru/profile/
👍8🔥4
DevOps | Обновления
Добавил урок.
Объекты K8s | ConfigMap [#505383]
Описание курса https://job4j.ru/profile/courses/35
Еще пока со скидкой - 7990 р.
Добавил урок.
Объекты K8s | ConfigMap [#505383]
Описание курса https://job4j.ru/profile/courses/35
Еще пока со скидкой - 7990 р.
👍8❤3
DevOps | Новые уроки
Объекты K8s | Secret [#505384]
Pipeline для K8s [#505385]
В уроке Pipeline для K8s [#505385] ваша любимка - настройка агента Jenkins через SSH ☺️
Объекты K8s | Secret [#505384]
Pipeline для K8s [#505385]
В уроке Pipeline для K8s [#505385] ваша любимка - настройка агента Jenkins через SSH ☺️
🔥9👍4
TalkSharp | Лингво-тренер | Короткие диалоги
Добавил в Лингво-тренера @talksharp_bot возможность установить роль для отработки коротких диалогов. Например, заказ такси или регистрация в отеле.
Бывает, нет настроения общаться на сложные темы, а такой режим отлично подойдет для тренировки повседневных фраз.
Он также отлично подойдет для начального уровня языка.
Также добавил график словарного заучивания. Настройки / Профиль / График словаря.
Также я заметил, что некоторые пользователи используют бота в текстовом режиме. Это, конечно, удобно, но все же язык — это звуки, а не графические символы.
Пробуйте @talksharp_bot
Добавил в Лингво-тренера @talksharp_bot возможность установить роль для отработки коротких диалогов. Например, заказ такси или регистрация в отеле.
Бывает, нет настроения общаться на сложные темы, а такой режим отлично подойдет для тренировки повседневных фраз.
Он также отлично подойдет для начального уровня языка.
Также добавил график словарного заучивания. Настройки / Профиль / График словаря.
Также я заметил, что некоторые пользователи используют бота в текстовом режиме. Это, конечно, удобно, но все же язык — это звуки, а не графические символы.
Пробуйте @talksharp_bot
👍3🔥1
Сеньор Java без опыта в многопоточности
Rutube https://rutube.ru/video/615e64b2695011ccbcbd11d60654eb14/
VK https://vk.com/wall298358674_511
Youtube https://youtu.be/AbumszjKYJY
Rutube https://rutube.ru/video/615e64b2695011ccbcbd11d60654eb14/
VK https://vk.com/wall298358674_511
Youtube https://youtu.be/AbumszjKYJY
VK
Петр Арсентьев. Пост со стены.
Сеньор Java без опыта в многопоточности
Тренды ИТ — https://job4j.ru/profile
Канал о IT ... Смотрите полностью ВКонтакте.
Тренды ИТ — https://job4j.ru/profile
Канал о IT ... Смотрите полностью ВКонтакте.
👍2
Системный дизайн | Код-ревью
Ниже приведен кусок кода из микросервиса.
Напишите в комментариях, какие могут возникнуть ошибки и предложите несколько возможных решений с указанием достоинств и недостатков.
Ниже приведен кусок кода из микросервиса.
Statistics statistics = statisticsRepository.findById(query).orElse(null);
if (s == null) {
s = new Statistics(query);
s.setNumberOfUses(0);
}
statistics.setNumberOfUses(statistics.getNumberOfUses() + 1);
statisticsRepository.save(statistics);
Напишите в комментариях, какие могут возникнуть ошибки и предложите несколько возможных решений с указанием достоинств и недостатков.
👍4
Курс DevOps | Новые уроки
Добавил урок по Helm
Обзор | Установка Helm Charts [#505387]
Залейте https://job4j.ru/profile/courses/35 еще со скидкой за 7990.
Добавил урок по Helm
Обзор | Установка Helm Charts [#505387]
Залейте https://job4j.ru/profile/courses/35 еще со скидкой за 7990.
❤6
Курс DevOps | Завершения раздела K8s
Обзор | Установка Helm Charts [#505387]
Объекты K8s | Ingress [#505386]
Создание Helm Charts для проекта job4j_k8s [#505388]
Тестовое задание | DevOps | 6 дней [#505389]
Последнюю задачу не дописал. Это тестовое задание, которые мне скинули на стажировку DevOps.
Обзор | Установка Helm Charts [#505387]
Объекты K8s | Ingress [#505386]
Создание Helm Charts для проекта job4j_k8s [#505388]
Тестовое задание | DevOps | 6 дней [#505389]
Последнюю задачу не дописал. Это тестовое задание, которые мне скинули на стажировку DevOps.
👍5❤4
Предложения и баги | Проекта IT-Тренер и Лингво-Тренер
Добавил issue трекеры для проектов @ITTalkBot и @talksharp_bot
https://github.com/peterarsentev/ITTalkBotFeaturesBugs/issues
https://github.com/peterarsentev/TalkSharpFeaturesBugs/issues
Буду раз услышать предложения или баги.
Добавил issue трекеры для проектов @ITTalkBot и @talksharp_bot
https://github.com/peterarsentev/ITTalkBotFeaturesBugs/issues
https://github.com/peterarsentev/TalkSharpFeaturesBugs/issues
Буду раз услышать предложения или баги.
GitHub
peterarsentev/ITTalkBotFeaturesBugs
Contribute to peterarsentev/ITTalkBotFeaturesBugs development by creating an account on GitHub.
❤4👍2👌2
Документация как источник истины
Доделаю курс по DevOps. Сегодня открыл два урока по версионированию.
1. Версии в Git | Git Tags [#505390]
2. Версии Rest API [#505395]
В целом, этот курс охватывает весь процесс разработки ПО. Изначально я думал, что он будет больше связан с элементами системного администрирования, но в итоге он охватывает все этапы разработки.
В этой статье хотел поговорить в целом о версионировании.
У меня был такой случай. В TrackStudio появился новый клиент. Причем это была крупная розничная сеть, поэтому мы старались его удержать. С их стороны с нами общался крайне скрупулезный человек. Из-за этого мне пришлось несколько дней тестировать TrackStudio с разными версиями JDBC под разные базы данных. В целом, этому человеку нужна была подробная спецификация нашего продукта, в том числе, какие версии баз данных мы поддерживаем и так далее. На тот момент я считал этого человека странным клиентом.
Но сейчас я могу сказать, что он требовал минимально допустимые документы. Без этих спецификаций использовать долгосрочный продукт будет сложно.
Здесь же хочу упомянуть частый случай конфликтов между командами серверной и клиентской частей, а также отделом тестирования. Если у тебя не работает интеграция или тесты, то, конечно, виноваты они.
Чтобы избежать таких проблем, нужно вести спецификацию, в которой указываются версии совместимых продуктов, проектов, API и программного окружения. Если коротко, то у вас должны быть версии всего, и они должны быть где-то зарегистрированы.
Приведу список, а вы можете проверить по своему проекту:
1. Версия проекта, указанная в Maven или Gradle.
2. Версии проекта, указанные в Git Tags.
3. Версии окружения, описанные в Ansible.
4. Версии REST API, описанные в Swagger. Лучше, если это отдельная спецификация, а не обертка поверх контроллеров.
5. Версии асинхронного API (например, Kafka).
6. Версии бэкапов баз данных.
7. Версии бинарных архивов.
8. Версии зависимостей (например, библиотек, фреймворков, плагинов) в проекте.
9. Версии контейнеров Docker, если проект использует контейнеризацию.
10. Версии инструментов CI/CD (например, Jenkins, GitLab CI, CircleCI).
11. Версии операционных систем и их зависимостей, используемых для разработки и развертывания.
12. Версии базы данных (например, PostgreSQL, MySQL) и используемых драйверов.
13. Версии библиотек и фреймворков для фронтенда (например, React, Angular, Vue).
14. Версии инструментов мониторинга и логирования (например, Prometheus, ELK stack).
15. Версии схем баз данных, описанные в миграциях (например, Flyway, Liquibase).
16. Версии системы управления конфигурациями (например, Consul, etcd).
17. Версии систем безопасности (например, VPN, аутентификация).
Частично эти элементы рассматриваются в курсе по DevOps: https://job4j.ru/profile/courses/35.
Пока еще со скидкой 7990 рублей.
Доделаю курс по DevOps. Сегодня открыл два урока по версионированию.
1. Версии в Git | Git Tags [#505390]
2. Версии Rest API [#505395]
В целом, этот курс охватывает весь процесс разработки ПО. Изначально я думал, что он будет больше связан с элементами системного администрирования, но в итоге он охватывает все этапы разработки.
В этой статье хотел поговорить в целом о версионировании.
У меня был такой случай. В TrackStudio появился новый клиент. Причем это была крупная розничная сеть, поэтому мы старались его удержать. С их стороны с нами общался крайне скрупулезный человек. Из-за этого мне пришлось несколько дней тестировать TrackStudio с разными версиями JDBC под разные базы данных. В целом, этому человеку нужна была подробная спецификация нашего продукта, в том числе, какие версии баз данных мы поддерживаем и так далее. На тот момент я считал этого человека странным клиентом.
Но сейчас я могу сказать, что он требовал минимально допустимые документы. Без этих спецификаций использовать долгосрочный продукт будет сложно.
Здесь же хочу упомянуть частый случай конфликтов между командами серверной и клиентской частей, а также отделом тестирования. Если у тебя не работает интеграция или тесты, то, конечно, виноваты они.
Чтобы избежать таких проблем, нужно вести спецификацию, в которой указываются версии совместимых продуктов, проектов, API и программного окружения. Если коротко, то у вас должны быть версии всего, и они должны быть где-то зарегистрированы.
Приведу список, а вы можете проверить по своему проекту:
1. Версия проекта, указанная в Maven или Gradle.
2. Версии проекта, указанные в Git Tags.
3. Версии окружения, описанные в Ansible.
4. Версии REST API, описанные в Swagger. Лучше, если это отдельная спецификация, а не обертка поверх контроллеров.
5. Версии асинхронного API (например, Kafka).
6. Версии бэкапов баз данных.
7. Версии бинарных архивов.
8. Версии зависимостей (например, библиотек, фреймворков, плагинов) в проекте.
9. Версии контейнеров Docker, если проект использует контейнеризацию.
10. Версии инструментов CI/CD (например, Jenkins, GitLab CI, CircleCI).
11. Версии операционных систем и их зависимостей, используемых для разработки и развертывания.
12. Версии базы данных (например, PostgreSQL, MySQL) и используемых драйверов.
13. Версии библиотек и фреймворков для фронтенда (например, React, Angular, Vue).
14. Версии инструментов мониторинга и логирования (например, Prometheus, ELK stack).
15. Версии схем баз данных, описанные в миграциях (например, Flyway, Liquibase).
16. Версии системы управления конфигурациями (например, Consul, etcd).
17. Версии систем безопасности (например, VPN, аутентификация).
Частично эти элементы рассматриваются в курсе по DevOps: https://job4j.ru/profile/courses/35.
Пока еще со скидкой 7990 рублей.
🔥11
Свидетели идиоматического Kotlin | Секта СИК
Где-то неделю назад я в чате написал, что свои хобби-проекты @ITTalkBot и @talksharp_bot начал на Java, а не на Kotlin.
Хотя платформу job4j.ru/profile и антиспам-бота @dionea_bot я делаю на Kotlin.
В этой статье я хотел бы объяснить причину.
В целом, стоит начать с того, что всех программистов, которые постоянно используют Kotlin, можно разделить на две группы. Деление происходит по тому, как этот язык используется. Первая группа относится к Kotlin как к Java++ — то есть улучшенной версии языка Java.
Приведу пример. Работа с шаблонами в Java 21 сделана через форматтеры.
Или
Второй вариант уже лучше, но он не сравнится с лаконичностью Kotlin:
Такие некрасивые элементы в языке Java заменяются идиомами в Kotlin.
Ниже приведён список распространённых идиом (приёмов) при программировании на Kotlin, которые помогут писать более читаемый, лаконичный и безопасный код:
- Использование `val` вместо `var` по умолчанию:
По возможности следует объявлять переменные неизменяемыми с помощью
- Null Safety и операторы `?.`, `?:`, `!!`:
Kotlin обладает встроенной поддержкой безопасного обращения с
- Data-классы:
Использование ключевого слова
- Smart Casts:
После проверки типа или на
- Extension Functions:
Позволяют добавлять новые функции в существующие классы без необходимости наследования или использования шаблонов проектирования.
- Функции высшего порядка и лямбда-выражения:
Kotlin широко использует лямбды и функции высшего порядка, такие как
- Запечатанные (sealed) классы:
Используются для ограничения иерархии классов, что особенно полезно при моделировании состояний или событий с исчерпывающим перечислением вариантов.
- Делегированные свойства (`by`):
Делегирование позволяет вынести повторяющуюся логику (например, ленивую инициализацию или наблюдение за изменением значений) в отдельные классы или функции.
- Компаньон-объекты:
Вместо статических членов, как в Java, используются объекты-компаньоны для размещения функций и переменных, связанных с классом, но не зависящих от экземпляров.
- Деструктуризация:
Позволяет распаковывать объекты и коллекции в отдельные переменные, что делает код более лаконичным и удобочитаемым.
- Перегрузка операторов:
Kotlin позволяет перегружать стандартные операторы для пользовательских типов, делая использование классов интуитивно понятным.
- Инфиксная запись:
Для функций с одним параметром можно использовать инфиксную запись, что улучшает читаемость при вызове функций, имитирующих операционные выражения.
И в сочетании с множеством библиотек и готовых решений экосистемы Java, Kotlin выглядит действительно классным решением. Именно в таком контексте я и применяю Kotlin. Он позволяет убрать шум из моего Java-кода. По такому же принципу я создавал курс по Kotlin: [https://job4j.ru/profile/courses/22](https://job4j.ru/profile/courses/22).
Но есть еще другая группа программистов, которых я называю сектой свидетелей идиоматического Kotlin.
Адептов этой секты крайне просто узнать. Если ты говоришь, что пишешь на Kotlin и показываешь своё решение, то любой код будет оценён одним и тем же вердиктом: ты пишешь как на Java, ты не умеешь писать на истинном Kotlin, ты не из «голубой крови».
Любые просьбы объяснить или показать истинный Kotlin-код ссылаются на реализованные библиотеки от компании JetBrains с комментарием: «Смотри исходники и учись».
Где-то неделю назад я в чате написал, что свои хобби-проекты @ITTalkBot и @talksharp_bot начал на Java, а не на Kotlin.
Хотя платформу job4j.ru/profile и антиспам-бота @dionea_bot я делаю на Kotlin.
В этой статье я хотел бы объяснить причину.
В целом, стоит начать с того, что всех программистов, которые постоянно используют Kotlin, можно разделить на две группы. Деление происходит по тому, как этот язык используется. Первая группа относится к Kotlin как к Java++ — то есть улучшенной версии языка Java.
Приведу пример. Работа с шаблонами в Java 21 сделана через форматтеры.
"""
User name: %s
"""
.formatted(user.name)
Или
STR."User name: \{ user.name }"Второй вариант уже лучше, но он не сравнится с лаконичностью Kotlin:
"User name: ${usern.name}"Такие некрасивые элементы в языке Java заменяются идиомами в Kotlin.
Ниже приведён список распространённых идиом (приёмов) при программировании на Kotlin, которые помогут писать более читаемый, лаконичный и безопасный код:
- Использование `val` вместо `var` по умолчанию:
По возможности следует объявлять переменные неизменяемыми с помощью
val, что улучшает безопасность и предсказуемость кода.- Null Safety и операторы `?.`, `?:`, `!!`:
Kotlin обладает встроенной поддержкой безопасного обращения с
null-значениями. Оператор безопасного вызова (?.) и оператор Элвиса (?:) помогают избежать ошибок, связанных с NullPointerException.- Data-классы:
Использование ключевого слова
data позволяет автоматически генерировать методы equals(), hashCode(), toString(), copy() и компонентные функции для классов, предназначенных для хранения данных.- Smart Casts:
После проверки типа или на
null переменные автоматически приводятся к нужному типу без явного кастинга.- Extension Functions:
Позволяют добавлять новые функции в существующие классы без необходимости наследования или использования шаблонов проектирования.
- Функции высшего порядка и лямбда-выражения:
Kotlin широко использует лямбды и функции высшего порядка, такие как
let, run, apply, also, для упрощения работы с объектами и цепочек вызовов.- Запечатанные (sealed) классы:
Используются для ограничения иерархии классов, что особенно полезно при моделировании состояний или событий с исчерпывающим перечислением вариантов.
- Делегированные свойства (`by`):
Делегирование позволяет вынести повторяющуюся логику (например, ленивую инициализацию или наблюдение за изменением значений) в отдельные классы или функции.
- Компаньон-объекты:
Вместо статических членов, как в Java, используются объекты-компаньоны для размещения функций и переменных, связанных с классом, но не зависящих от экземпляров.
- Деструктуризация:
Позволяет распаковывать объекты и коллекции в отдельные переменные, что делает код более лаконичным и удобочитаемым.
- Перегрузка операторов:
Kotlin позволяет перегружать стандартные операторы для пользовательских типов, делая использование классов интуитивно понятным.
- Инфиксная запись:
Для функций с одним параметром можно использовать инфиксную запись, что улучшает читаемость при вызове функций, имитирующих операционные выражения.
И в сочетании с множеством библиотек и готовых решений экосистемы Java, Kotlin выглядит действительно классным решением. Именно в таком контексте я и применяю Kotlin. Он позволяет убрать шум из моего Java-кода. По такому же принципу я создавал курс по Kotlin: [https://job4j.ru/profile/courses/22](https://job4j.ru/profile/courses/22).
Но есть еще другая группа программистов, которых я называю сектой свидетелей идиоматического Kotlin.
Адептов этой секты крайне просто узнать. Если ты говоришь, что пишешь на Kotlin и показываешь своё решение, то любой код будет оценён одним и тем же вердиктом: ты пишешь как на Java, ты не умеешь писать на истинном Kotlin, ты не из «голубой крови».
Любые просьбы объяснить или показать истинный Kotlin-код ссылаются на реализованные библиотеки от компании JetBrains с комментарием: «Смотри исходники и учись».
👍4
В 2018 году я также увлёкся лозунгами языка нового уровня с новой парадигмой. До этого я уже поработал в проекте со Scala, но продолжать на нём я не хотел. Посмотрев все выступления Андрея Бреслава, прочитав и пройдя его курсы, я понял, что не могу уловить смысл новой парадигмы. Есть идиомы, нет парадигмы, нет принципов. В одном из докладов я всё же нашёл примерный манифест адептов СИК:
- Гибридная парадигма: сочетание объектно-ориентированного и функционального программирования.
- Разделение данных и функций, а не создание объектов с состояниями и методами.
- Предпочтение неизменяемых состояний для повышения безопасности и надёжности.
- Избегание наследования, если оно используется только для сокращения кода. Вместо этого применяются расширения.
- Использование функций там, где можно обойтись без классов.
- Применение областей видимости для управления доступом к полям и функциям.
Пройдем по ним. Сначала выделим те принципы, которые я также пропагандирую в своём курсе и считаю крайне важными:
1. Неизменяемые состояния. Это крайне важный принцип, который стоит учитывать в любом языке.
2. Наследование. У меня даже был урок в курсе, где я однозначно указывал, что наследование — это зло. Я придерживаюсь этого мнения до сих пор, но не так радикально. Его можно использовать, например, в описании моделей данных, но не в сервисах.
3. Зона видимости. Я бы даже добавил, что здесь речь идёт не о зоне видимости, а о зоне ответственности. То есть, это принципы, описанные в книге Роберта Мартина «Чистая архитектура», а зона видимости — это просто частный случай того, как это можно реализовать.
Гибридная парадигма — это общие слова, ведь сегодня любой современный язык позволяет писать в трёх парадигмах.
Теперь обсудим то, чего не хватает.
Разделение данных и функций, а не создание объектов с состояниями и методами.
Использование функций там, где можно обойтись без классов.
Давайте попробуем спроектировать классический проект Rest API без базы данных.
Предположим, что мы будем использовать классы только для кортежей данных, а весь остальной код будем описывать функциями.
Я хотел описать функцию, которая является точкой входа в приложение и сразу возвращала объект Server. Но так нельзя, потому что этот объект будет хранить состояние сервера. Давайте предположим, что функция ничего не возвращает.
Ха. Получилось. Нам нужно добавить обработчики запросов.
Поправим server:
В целом получается, что можно продолжать писать так. В проекте Job4j.ru я поступил именно так: не стал использовать никакие библиотеки и решил написать всё сам с нуля.
Конечно, можно было бы использовать библиотеки, но важно отметить, что в истинном Kotlin следует использовать только библиотеки, описанные в манифесте Kotlin; нельзя взять Spring Boot, Spring JPA — нужно выбрать альтернативное решение, написанное на Kotlin. Например, Ktor + Exposed.
- Гибридная парадигма: сочетание объектно-ориентированного и функционального программирования.
- Разделение данных и функций, а не создание объектов с состояниями и методами.
- Предпочтение неизменяемых состояний для повышения безопасности и надёжности.
- Избегание наследования, если оно используется только для сокращения кода. Вместо этого применяются расширения.
- Использование функций там, где можно обойтись без классов.
- Применение областей видимости для управления доступом к полям и функциям.
Пройдем по ним. Сначала выделим те принципы, которые я также пропагандирую в своём курсе и считаю крайне важными:
1. Неизменяемые состояния. Это крайне важный принцип, который стоит учитывать в любом языке.
2. Наследование. У меня даже был урок в курсе, где я однозначно указывал, что наследование — это зло. Я придерживаюсь этого мнения до сих пор, но не так радикально. Его можно использовать, например, в описании моделей данных, но не в сервисах.
3. Зона видимости. Я бы даже добавил, что здесь речь идёт не о зоне видимости, а о зоне ответственности. То есть, это принципы, описанные в книге Роберта Мартина «Чистая архитектура», а зона видимости — это просто частный случай того, как это можно реализовать.
Гибридная парадигма — это общие слова, ведь сегодня любой современный язык позволяет писать в трёх парадигмах.
Теперь обсудим то, чего не хватает.
Разделение данных и функций, а не создание объектов с состояниями и методами.
Использование функций там, где можно обойтись без классов.
Давайте попробуем спроектировать классический проект Rest API без базы данных.
Предположим, что мы будем использовать классы только для кортежей данных, а весь остальной код будем описывать функциями.
fun server(port: Int) : ??
Я хотел описать функцию, которая является точкой входа в приложение и сразу возвращала объект Server. Но так нельзя, потому что этот объект будет хранить состояние сервера. Давайте предположим, что функция ничего не возвращает.
fun server(port: Int) {
var socket = ServerSocket(port)
while (socket.accept()) {
}
}Ха. Получилось. Нам нужно добавить обработчики запросов.
fun route(): Map<String, (req: Req) -> Resp> {
return mapOf(
"home" to home(req),
"about" to about(req)
)
}Поправим server:
fun server(port: Int, routes: Map<String, (req: Req) -> Resp>) {
var socket = ServerSocket(port)
while (socket.accept()) {
routes[socket...]
}
}В целом получается, что можно продолжать писать так. В проекте Job4j.ru я поступил именно так: не стал использовать никакие библиотеки и решил написать всё сам с нуля.
Конечно, можно было бы использовать библиотеки, но важно отметить, что в истинном Kotlin следует использовать только библиотеки, описанные в манифесте Kotlin; нельзя взять Spring Boot, Spring JPA — нужно выбрать альтернативное решение, написанное на Kotlin. Например, Ktor + Exposed.
👍3
import io.ktor.application.*
import io.ktor.http.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.SchemaUtils
// Определение таблицы пользователей
object Users : Table() {
val id = integer("id").autoIncrement()
val name = varchar("name", length = 50)
override val primaryKey = PrimaryKey(id)
}
// Инициализация подключения к базе и создание таблицы с тестовыми данными
fun initDB() {
// Подключаем in-memory базу H2
Database.connect("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;", driver = "org.h2.Driver")
transaction {
// Создание таблицы
SchemaUtils.create(Users)
// Добавление тестовой записи
Users.insert {
it[name] = "John Doe"
}
}
}
fun main() {
// Инициализируем базу данных
initDB()
// Запускаем Ktor-сервер
embeddedServer(Netty, port = 8080) {
routing {
// Корневой маршрут
get("/") {
call.respondText("Hello, Ktor with Exposed!", ContentType.Text.Plain)
}
// Маршрут для получения списка пользователей из базы
get("/users") {
// Чтение данных из таблицы Users
val users = transaction {
Users.selectAll().map { "${it[Users.id]}: ${it[Users.name]}" }
}
call.respondText(users.joinToString(separator = "\n"), ContentType.Text.Plain)
}
}
}.start(wait = true)
}
Но код всё равно будет написан в идеологии, которую я привёл выше, то есть функция принимает объекты, которые содержат состояние (например, пул коннектов), что в целом уже нарушает описанный манифест.
fun poolDb() : Pool
fun server(port: Int, routes: Map<String, (req: Req) -> Resp>, poolDb) {
var socket = ServerSocket(port)
while (socket.accept()) {
routes[socket...]
}
}
То есть, не всё так гладко.
Я не затронул отдельную тему — корутины. Корутины действительно являются одним из хедлайнеров в языке Kotlin. Отдаю своё уважение команде Роман Елизаров. Но, как я уже писал в другом посте, существует множество сеньоров и тимлидов, которые даже обычный thread pool не используют в проектах. Поэтому корутины — это не элемент для большинства программистов.
А теперь минусы.
Если рассматривать первую группу программистов, которые относятся к Kotlin как к Java++, то тут можно сказать многое. Перейти на использование Kotlin в таком ключе несложно. У меня много примеров выпускников, которые сразу начинали работать в проекте с Kotlin, даже несмотря на то, что у них 0 опыта в этом языке.
А теперь перейдём к минусам второй группы.
Программирование — это чисто прикладная область. Программисты получают свои деньги за решение технических проблем. То есть здесь нет понятия эстетики кода — есть исключительно прагматические критерии.
Первый минус. Переучить программистов писать в идиологии истинного Kotlin крайне дорого. В целом, переучивать программиста — это крайне дорогая затея, проще обучить с нуля. Это связано с инертностью нашего восприятия. Мы тяготеем к комфорту, поэтому воспринимаем новые идеи в штыки.
Второй момент. Даёт ли использование истинного Kotlin значительные плюсы? Не особо. Нам всё равно нужно придумывать и проектировать архитектуру, писать тесты, использовать системы статического анализа, заниматься приемочным и ручным тестированием. Код становится проще поддерживаемым? Уверяю вас, код становится проще поддерживаемым, если его спроектировали правильно, по принципам чистой архитектуры, а не с помощью идиом языка.
Третий момент. Найм. Я могу посадить любого ученика на свой проект, который прошёл блок SQL [https://job4j.ru/profile/courses/7](https://job4j.ru/profile/courses/7), и он сразу будет выполнять задачи, потому что ему будет понятен код.
А что там с рынком? Ищем по слову ktor — 48 вакансий. Удачи, ребята: если что, всегда можно пойти курьером в ДОДО или завести пасеку.
👍8
Подведу итог.
В целом, язык Kotlin — прекрасный язык, который позволяет писать выразительный код в экосистеме Java. Его стоит попробовать любому Java-программисту, но ожидать от него решения глобальных проблем в проектах не стоит. Это не волшебная палочка, а всего лишь удобный инструмент.
В целом, язык Kotlin — прекрасный язык, который позволяет писать выразительный код в экосистеме Java. Его стоит попробовать любому Java-программисту, но ожидать от него решения глобальных проблем в проектах не стоит. Это не волшебная палочка, а всего лишь удобный инструмент.
👍11
Лингво-тренер | Правки
Поправил поведение "рекомендации". Теперь отвечает уместно.
В целом заменил, что активность использования приложения, точно такая же, как и в проекте Job4j. То есть волнами. Видимо эта нормально для учетного процесса.
Сегодня с ботом тренировался так. Выставил ему роль "Ресепшен в отеле" и отрабатывал ситуацию резервирования номера для семьи. Я указывал, что ему нужно в номере, спрашивал про скидки, спрашивал, про детскую кроватки и тд. Потом просил отменить бронирование и перебронировать на другую дату. Просил скидки и залог. В целом забавлялся, как мог.
Пробуйте @talksharp_bot
Поправил поведение "рекомендации". Теперь отвечает уместно.
В целом заменил, что активность использования приложения, точно такая же, как и в проекте Job4j. То есть волнами. Видимо эта нормально для учетного процесса.
Сегодня с ботом тренировался так. Выставил ему роль "Ресепшен в отеле" и отрабатывал ситуацию резервирования номера для семьи. Я указывал, что ему нужно в номере, спрашивал про скидки, спрашивал, про детскую кроватки и тд. Потом просил отменить бронирование и перебронировать на другую дату. Просил скидки и залог. В целом забавлялся, как мог.
Пробуйте @talksharp_bot
👍6