Pull to refresh

Джунам в Swift: как делать нейминг правильно?

Reading time8 min
Views6.6K

Занимаясь на курсах или обучаясь по различным туториалам, книгам и статьям, начинающие разработчики не слишком заботятся о том, как называть свои константы, переменные, классы, протоколы и т.п. во время написания кода. А ведь код пишется прежде всего для людей, а не для машины (машина понимает лишь язык нулей и единиц). Соответственно, для того, чтобы работать в команде, нужно позаботиться о том, чтобы код был понятен другим разработчикам (или понятен самому себе спустя несколько месяцев). Понятный код - один из важнейших критериев отбора джунов на работу. Ни один работодатель не захочет брать на работу кодера, у которого в коде "без бутылки не разберешься".

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

Передо мной стояла задача переделать экран настройки текста. Нужно было убрать/заменить некоторые кнопки, изменить некоторую логику и тп. В целом ничего сложного, но задача заняла гораздо больше времени, чем могла бы, именно из-за проблемного нейминга. Вся верстка была сделана в коде, соотвественно, чтобы выполнить задачу, сначала нужно было разобраться в том, какой код соответствует содержимому на экране.

Итак, перед нами экран редактирования текста.

Нижняя панель с настройками - это отдельный чайлд контроллер, помещенный в специальный контейнер поверх нашего экрана. Вот эту панель мы и рассмотрим.

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

Задача: А) убрать шрифты, Б) на их месте расположить кнопки выравнивания и цвета, В) вытащить кнопки стиля, чтобы они всегда были видны, Г) убрать ползунок размера.

Вроде всё просто. 🙂 Всего-то надо удалить несколько элементов, а остальные переставить. "Ща за 5 минут сделаю!" 😎

Смотрим в код. Верстка выглядела вот так:

Немало тут элементов... 👀 Количество строк тоже несколько настораживает. Но попробуем угадать, что тут есть что 🙂 🎰

  1. separator (326 строка)- это, судя по названию, линия под заголовком

  2. minSliderImage и maxSliderImage (333 и 335) - это, очевидно картинки по краям слайдера

  3. slider (334) - это сам слайдер внизу

Вот и всё, что ясно из этого экрана 💁‍♂️ Конечно, можно (и нужно!) посмотреть иерархию вьюх в дебаггере и попробовать покликать там по элементам на экране, чтобы стало немного понятнее. Но это дополнительное время, да ещё и не всегда от созерцания иерархии становится всё понятно. Но об этом в конце статьи. А сейчас попытаемся понять по коду что есть что. Погнали по порядку.

  1. collectionView (327 строка) - Судя по экрану, это может быть: A) коллекция из названий шрифтов, а может быть Б) вторая строка с кнопкой карандаша, кнопками alignment и кнопками цвета. Или это В) коллекция, которая появляется, когда нажимаешь кнопку с карандашом.

  2. stackView (328) - Ещё одно малоговорящее название. Мне кажется допустимым такое абстрактное название давать элементам, если они чуть ли не единственные на экране. Но когда верстка сложная, под этим названием может скрываться всё, что угодно.

  3. collectionContainerView (329) - Казалось бы, у нас есть коллекция, так зачем для неё ещё и контейнер? Ну, разные могут быть причины. Может, какое-то особое расположение надо. Ок, пусть так.

  4. viewForBoldButton (330) - Судя по названию, это какая-то отдельная вьюха для кнопки "жирный". Возникает вопрос: "А почему только для неё? Ведь у нас есть ещё и кнопки "italic", "bold italic", "stroke", "stroke bold" и "stroke bold italic". В общем, вопросов пока только прибавляется. 🧐

  5. boldTextButton (331) - А вот и сама кнопка "жирный". Интересно, зачем её надо отдельно помещать на экран? Разве она не вкупе с остальными должна быть? И самое странное - зачем под неё отдельное вью добавлено строкой выше, но сама кнопка добавляется не на это вью, а просто на вью экрана? 👀

  6. bottomStackView (332) - О, уже есть какая-то подсказка! Очевидно, этот стек вью находится где-то внизу. Скорее всего, это должен быть стек вью для слайдера и двух картинок, обозначающих размер шрифта. Так, стоп! А зачем тогда они были добавлены на вью, а не на этот стек вью? Они же должны быть arrangedSubviews этого нижнего стек вью, разве нет? Что-то не сходится... Ладно, едем дальше.

  7. backgroundContainerView (336) - Вроде бы и подсказка есть, да вот что-то не совсем очевидно, что это за бэкграунд такой. Если бы это был какой-нибудь заблюренный фон, то логично было бы тогда все остальные элементы поместить в этот контейнер. Но нет же, они помещены на вью, а сам контейнер помещен поверх них. Так что же это тогда?

Вот вьюхи закончились, но остались ещё вопросы: где кнопки Cancel и Save, где сам заголовок? В итоге мы имеем очень много вопросов.

Что же тут на самом деле?

Спустя какое-то время ковыряния я выяснил следующее:

  1. Я угадал только с сепаратором, слайдером и картинками, обозначающими размер шрифта.

  2. Кнопки Save и Cancel вместе с заголовком лежат на том самом ноунейм "stackView" (пункт 5 из списка выше). Что ж, в таком случае его стоило бы назвать "headerStackView" или "titleStackView" или как минимум "topStackView". Логично?

  3. Ноунейм collectionView - Это всё таки коллекция с названиями шрифтов! Ура! Ну, почему бы её не назвать было как-нибудь "fontNameCollectionView" или хоть как-нибудь иначе, что подсказало бы, что это такое, и где это искать? (В тот момент мысли в голове проносились не столь цензурные 😅)

  4. collectionContainerView - Это не контейнер для вышеописанной ноунейм коллекции, как можно было подумать, а контейнер для чайлда, который содержал в себе коллекцию с ячейками, обозначающими стили шрифта. 🤯 Оставим в стороне это неожиданное решение, разберем только нейминг. Какое-нибудь textStyleCollectionContainerView подошло бы куда лучше. Как считаете?

  5. viewForBoldButton и boldTextButton - как выяснилось, это та самая кнопка с карандашом. Я уже не помню, выяснил ли я тогда, зачем надо было эту кнопку лепить из двух вьюх, и для чего тут какая-то viewForBoldButton, или не выяснил. Но весьма был удивлён, почему бы не назвать эти вьюхи textStyleButton или, например, pencilButton, чтобы хоть как-то опознать их на экране.

  6. bottomStackView - оказалось, что это стек, который состоит из двух объектов: сегмент контрола с капитанским названием segmentControl и стека colorsStackView (Ура, наконец-то что-то понятное!). segmentControl - под этим ничего не значащим названием скрывалась настройка выравнивания текста. Почему стек называется bottomStackView мне непонятно, он ведь не внизу. Наверное, для того, чтобы показать, что это не просто стэк вью, как было в заголовке, а "нижний" стек вью. Чтобы хоть как-то отделить его от ноунейм стек вью из пункта 2. Но на таком сложном экране, как вы поняли, сложно вообще понять, что есть что. Так что такое название вообще не помогает. Вот если бы этот стек назывался alignmentAndColorsStackView, то вопросов бы не возникло. 👍

  7. backgroundContainerView - неожиданно, но им оказался контейнер для отображения чайлда с настройками цвета. Вдумайтесь, вот как тут вообще связаны цвет и бэкграунд? Гугл переводчик говорит, что "цвет" - это color. 🙂 Поэтому данный контейнер должен был называться как минимум colorSettingsContainerView или что-то схожее, что однозначно определит его предназначение. Но назвать его бэкграунд? Как-то странно. Изучив код можно сделать вывод, что такое название скорее всего продиктовано не менее странным названием чайлда, который отвечает за настройки цвета: BottomBackGroundController, который кто-то создал до этого. Редактирование цвета названо как "нижний контроллер бэкграунда". Странно, не так ли? Просто непонятно, причем тут цвет и расположение с бэкграундом.

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

Вероятнее всего, что, как и все мы когда-то, автор кода не сильно задумывался над тем, что кто-то вообще будет в этот код лезть и что-то там править. По неопытности о будущем не сильно задумываешься, тем более, когда горят сроки. Тем не менее этот код очень удачно подвернулся для того, чтобы наглядно показать, каких ошибок следует избегать.

Читая блоги и книги маститых кодеров, я встретился с одной очень интересной мыслью: "Самое сложное в программировании - это правильный нейминг". 😈 Не помню, кто так сказал. Возможно, так сказал дядюшка Боб (Роберт Мартин). Если знаете, напишите в комментариях 😉

Если сложности с неймингом

Итак, смею надеяться, теперь вы поняли, как сильно может запутать неподходящий или ничего не говорящий нейминг. Так что, уважаемые джуны, именуйте сущности правильно, по смыслу. Это действительно непросто, и порой не всегда быстро приходит в голову удачный нейминг. Иногда просто нет времени долго думать над названием. В таком случае ставьте три слэша над переменной/константой/(подставьте что-то своё) и пишите документацию к сущности при её объявлении. То есть напишите там, для какой цели создана эта штука, где и когда она используется.

Добавляем документацию
Добавляем документацию

Эту документацию потом можно из любого места программы посмотреть, тапнув с зажатым Option на переменную/константу(ну, вы поняли). Прямо как у Apple ко всем её публичным переменным, константам, классам, структурам, энамам и протоколам.

Клик при зажатом Option выдаёт описание
Клик при зажатом Option выдаёт описание

Кстати, удобнее всего пользоваться сочетанием клавиш Cmd+Option+/ при курсоре, расположенном прямо на строке объявления вашей сущности. Тогда Xcode сделает за вас грязную работу:

Вам останется только заполнить соответствующие поля.

Начинайте уже сегодня

По моей инициативе мы с коллегами с некоторых пор всегда пишем документацию ко всем контроллерам/классам/структурам, открытым свойствам и методам. Особенно, если их названия не кристально ясны и документация к ним не будет похожа на заметки от Капитана Очевидности. Чтобы максимально не быть кэпами мы пишем для чего, когда и где используется та или иная штука. Сначала непривычно, но потом это сильно ускоряет работу. Впоследствии, когда натыкаешься на такую документацию, она может сработать как спасательный круг и сэкономит вам и коллегам массу времени.

Если вы (или у вас в команде) так ещё не делается, то начните с себя. Через какое-то время коллеги оценят, и вам будет проще протолкнуть инициативу, чтобы так делали все в вашей команде. Что касается тестовых работ, то такой подход ваши будущие работодатели точно оценят!

Советы по неймингу

Рекомендую ознакомиться с принципами нейминга в наиболее популярном код-стайле от Рея Вандерлиха: https://github.com/raywenderlich/swift-style-guide#naming

Там всё подробно написано. Вот некоторые рекомендации:

  1. Все булевые значения именовать с какой-либо из "приставок": is, has, should, will, can, did, was и т.п. Прям как у Эпл isHidden, hasPrefix и тп. То есть "приставка" всегда в начале: не buttonIsHidden, а isButtonHidden.

  2. Протоколы делегатов именовать с "суффиксом" Delegate. Например, MyAwesomeViewControllerDelegate.

  3. Протоколы из категории "обладающий способностью" называть с "суффиксом" -able или -ible. Например, что-то вроде ConsolePrintable.

  4. Протоколы, описывающие что-то (какой-нибудь сервис или обработчик) должны быть написаны как существительные. Например, протокол какого-нибудь хранилища лучше всего так и назвать Storage (а не StorageProtocol, как любят писать джуны). И тогда ваша сущность, построенная на этом протоколе можно назвать StorageImpl: Storage (от слова "implementation" - реализация). Или какой-нибудь MainStorage или LocalStorage и т.п. Если это тестовое хранилище (моковое), оно может иметь название MockStorage: Storage и т.п.

Про поиск в иерархии вьюх

Не всегда в иерархии (Debug View Hierarchy) становится всё сразу понятным. Потому что в коде, к примеру, ваш стек вью именуется bottomStackView, а в иерархии он будет просто UIStackView. Соответственно, если стек-вьюх на экране много, то трудно понять, где какая. Но есть простой способ: добавить значение в строковое свойство accessibilityIdentifier. Это свойство есть у всех вьюх (вроде бы оно используется в Универсальном доступе при проблемах со зрением, но это неточно), и его очень удобно использовать именно для дебага в иерархии вьюх.

Прописываем accessibilityIdentifier
Прописываем accessibilityIdentifier
Опознаём вьюху в дебаггере по идентификатору
Опознаём вьюху в дебаггере по идентификатору

Спасибо за уделённое время! Happy coding! :]

Tags:
Hubs:
+7
Comments4

Articles