Предыдущая статья была довольно фундаментальной, так что в этой статье перейдём к более приземлённому и практичному примеру. И так как я никак не могу пройти мимо таких мастодонтов, как объекты-менеджеры.
Чтобы наиболее ярко отобразить проблему, я приведу пример одного из особенных представителей таких менеджеров - main.dart.
Задачи, выполняемые в данном файле объектом MyAppState:
- инициализация книжных закладок, расчёт отображаемой закладки
- инициализация сервиса оплаты
- запрос ежедневных данных, их сортировка
- запрос книг из локального хранилища, контроль за загрузкой книг с сервера
- настройка световой темы приложения
- настройка аналитика
- загрузка и компрессия ресурсов
- загрузка списка музыки, инициализация плеера
- настройка push-уведомлений
- инициализация настроек приложения
- загрузка музыкального трека по умолчанию
- загрузка менеджера баз данных
- настройка начального экрана
- получения ссылок на загрузку ресурсов
Количество строк в файле - 800.
Фух. Даже простое перечисление функций в этом файле заняло у меня около 10 минут, при том, что я заранее знал его функциональность.
А теперь представим себе ситуацию, что работу с проектом, где находится такой менеджер, поручили любимцу читателей этого курса и новичку на проекте Альфредо. Его задача - фикс бага, суть которого в выборе неверного музыкального трека по умолчанию после второго запуска приложения.
Альфредо ещё не знаком с проектом, и поэтому поиск он начинает в первую очередь с поиска чего-нибудь с именем AudioService, AudioPlayer и т.п. Альфредо даже находит плеер, но там он находит лишь воспроизведение трека, переданного при инициализации. Затем, уже изучив полностью AudioPlayer, он переходит к поискам внутри менеджера, который и инициализирует сам плеер. Поиски немного затягиваются...
И вот, к моменту, когда Альфредо наконец добирается до метода, который работает некорректно, он уже потратил половину рабочего дня. И чем больше такой объект-менеджер, тем больше и больше будет время анализа кода и поиска проблемного места в нём.
Сделаю небольшое обобщение. Проблема проекта Альфредо - объекты не отражают свою работу, а также большие объекты, которые выполняют больше работы, чем должны.
Избегать объектов с множеством обязанностей можно в мире теоретическом, но на практике такого избежать не получится. Но если у вас формируется объект-менеджер, то это может служить показателем того, что у такого объекта слишком много обязанностей.
Итак, попробуем разобрать, чьи обязанности забрал на себя жадный до власти MyAppState:
- Image manager
- Book manager
- Data base manager
- Audio manager
И при этом, каждый из этих четырех объектов сам занимается несколькими активностями, которые при должном объеме, и сами могли бы быть разбиты на более мелкие части, как, например,Image manager мог бы разбиваться на:
- Image fetch
- Image compression
- Image storage
После проведения такого разбиения, можно заметить, что некоторые из объектов имеют общую схожую функциональность, от чего их можно сделать обобщенными. Правда, данную тему я затрону в будущем в материалах, посвященным обобщениям.
Также важно, чтобы ваши типы и объекты довольно точно описывали собственную функциональность. Это важно не только для читаемости и понимания кода, но также может служить проверкой на то, что ваш объект не переусложняется, ведь объем его работ все ещё описывается его названием. Причем желательно, чтобы название, которое вы даёте вашим объектам и типам, описывали не то, как он используется, а то, что он делает на практике. От этого выигрывает и информативность, и переиспользование кода.
Очередное подведение итогов. Огромные объекты - зло, с которым можно бороться. Иногда приходится пойти на компромисс, но этот компромисс стоит искать лишь тогда, когда проблема не в неграмотной структуре кодовой базы, а в её большом размере. Грамотное разбиение упрощает понимание кода и открывает новые возможности для переиспользования кода. И нужно всегда следить, чтобы наименования описывали функциональность объекта, а функциональность описывалась наименованием.