Третий выпуск спецпроекта, в котором опытные разработчики Яндекса продолжают разбирать код стажёров и джунов и объяснять, что в нём сделано хорошо, а что ещё можно улучшить.
Сегодня под микроскоп попадает WeatherApp — приложение прогноза погоды с ретровизуалом. Арсений Носов, Android-разработчик в команде Алисы и Умных устройств, тщательно разбирает код и делится полезными советами по работе с ним.
Ссылка на код: https://github.com/ArkhamDm/WeatherApp
Readme кода на Kotlin: https://github.com/ArkhamDm/WeatherApp
Не забывайте делиться мнением о разборе в комментариях и присылать ссылки на свои репозитории. Самый интересный код попадёт в наш следующий выпуск!
Что понравилось
- Многомодульная структура: отдельные модули для API геокодинга, погоды и базы Room; названия модулей отражают ответственность и упрощают навигацию по проекту.
- Грамотный Retrofit‑builder и Interceptor для автоматической подстановки API‑ключа в query, что убирает бизнес‑логику из вызовов.
- Кэширование/офлайн: попытка сначала получить погоду из API, затем падение в Room при проблемах с сетью выглядит логично.
Ключевые проблемы архитектуры
- Нет domain‑слоя: UI ViewModel ходит напрямую в data/repository, при этом “data” фактически содержит доменные entity; стоит ввести явный домен/интеракторы и переименовать сущности корректно.
- Дублирование логики: одинаковые interceptors для разных API и повтор преобразований погодных условий в нескольких местах — это нужно выделять и переиспользовать.
- Закомментированный и неиспользуемый код (geocoding API/методы) лучше удалить; всё хранится в истории коммитов.
Кодстайл и надёжность
- Жёсткие
!!и потенциальные NPE: вместо двойного восклицания использовать безопасные обращения/ранний возврат, а nullable‑данные сделать non‑null на границах доступа к БД/сети. - Хардкоды строк и ключей: русские строки и ключи аргументов/SharedPreferences нужно вынести в ресурсы/константы для локализации и целостности.
- Координаты как Pair/FloatArray — слабая типизация; лучше ввести отдельный тип (value class/data class) для координат.
UI и компоненты
- Кастомная
TypewriterTextView: жёстко зашитые параметры (delay, textSize), deprecatedHandler(); следует вынести атрибуты в styleable, использовать MainLooper и сделать виджет конфигурируемым. - RecyclerView: есть самописный LayoutManager, но параметры не конфигурируемы; передавать их через конструктор для переиспользования.
- Вьюхолдеры: логику биндинга лучше инкапсулировать в метод
bind()ViewHolder, а не делать это в onBindViewHolder.
Навигация и фрагменты
- Диалоги/фрагменты: константы ключей аргументов и companion object держать внизу; убрать хардкоды строк; уменьшить вложенность веток состояния (setData/setError/setLoading).
- MapFragment/Activity: убрать
!!, вынести работу с координатами/преференсами из Activity во фрагменты/слой данных; проверять обе координаты, а не только долготy.
Данные и модели
- WeatherInfo как data class с var‑полями, которые позже мутируются в воркере/VM, приводит к неочевидным hashCode/equals; выполнить преобразования при создании модели и держать её иммутабельной.
- Нотификации: не предполагать, что список не пуст; добавить защиту от пустых коллекций; вынести channelId в единый источник (класс/константа).
Gradle и зависимости
- В app build.gradle устаревшие target/source compatibility (Java 1.8); рекомендуется обновление и единый version catalog для синхронизации версий артефактов.
- BuildConfigFields корректно используются для переключения базовых URL между debug/release, что удобно для прод/бета окружений.
Мелкие, но важные улучшения
- Трейлинг‑запятые в Kotlin снижают размер диффов в ревью и упрощают добавление элементов в коллекции/аргументы.
- Чистить лишние отступы/двойные пробелы; убрать неиспользуемые тестовые заготовки и явно не нужные exported активности без intent‑фильтров.
Итоговые советы автора
- Не дублировать код; выносить общее в переиспользуемые компоненты и модули.
- Разделять слои ответственности (UI, domain, data) и держать функции маленькими и понятными.
- Делать изменения расширяемыми, очевидными и локализуемыми; избегать хардкодов и небезопасных допущений о данных.

