Записки iOS программиста о его молотках, кувалдах и микрометрах

в 21:05, , рубрики: ios development, iOS разработка, tldr, инструменты разработчика, разработка под iOS, Совершенный код

В один прекрасный момент, когда на собеседованиях меня уже убеждали, что я senior iOS developer — у меня возникло ощущение, что я уперся. Пишу похожий код, решаю задачи похожими способами и ощущение, что непонятно, куда развиваться дальше. Я думаю, с этой проблемой сталкивался не один я — нехваткой новых идей, концепций, направлений. Я бы хотел рассказать вам о тех инструментах и фреймворках, которые помогли преодолеть мне это ощущение.

Думаю, большинство из здесь присутствующих разработчиков читала таких ребят, как банду четырех. Все, хотя бы на собеседованиях, слышали слово паттерн, кто-то более (или менее) везучий слышал слова пострашнее — императивный, функциональный, монада, реактивность и другие ужасы. Вообще, довольно много ярких и интересных идей ходит в мире разработки ПО и, к счастью, далеко не все из них существуют только в виде словесных абстракций. Я бы хотел в этой статье немного рассказать не столько о прикладном инструментарии (хотя именно с ним мы и сталкиваемся большую часть рабочего времени), сколько о примерах инструментария, для использования которых нужно осмысление, которое существенно помогает в дальнейшем. Я бы хотел рассказать о том, как (и какие) инструменты изменяют сам процесс проектирования, написания кода, по крайней мере сделали это для меня.
Записки iOS программиста о его молотках, кувалдах и микрометрах - 1

Данная статья, на мой взгляд, будет достаточно сложна для разработчиков уровня junior, однако здесь перечислены замечательные инструменты — чтение кода и примеров применения которых может очень сильно подтолкнуть вас к развитию. Более опытным разработчикам, наверняка, существенная часть этой информации известна, но, я полагаю, что что-то полезное найдется и для вас. Хотя бы потому, что я, к своему стыду и ужасу, с большей частью из описанного в статье познакомился достаточно поздно.

Вообще, как показывает практика — самые полезные решения, как правило, достаточно просты. Основные оптимизации практически всегда заключаются не в выборе алгоритма c асимптотикой О(n) вместо O(n log n), а в выборе способа организации кодовой базы. Если ты тратишь меньше времени на борьбу с собственным кодом, ты тратишь больше времени на улучшение оного. Поэтому последнее время способам организации кода я стараюсь уделять больше времени, чем 201-му поду, реализующему PullToRefresh на UITableView. И я хотел бы поделиться некоторыми из таких известных мне способов.

  • В первой и самой маленькой главе я начну с трех не очень сложных, но полезных фреймворков. Скорее для того, чтобы было с чего начать, не ныряя сразу на всю глубину.
    • cocoaPods
    • MagicalRecord
    • Promise Kit

  • Во второй главе я перечислю фреймворки, которые можно класть в основу вашего приложения или какой-то существенной его части — идеи и концепции которые достаточно серьезно затрагивают весь процесс разработки. Для того чтобы начать ими пользоваться в полной мере я бы рекомендовал написать хотя бы по паре небольших приложений на их основе — потому что они заметно влияют на мышление при дизайне новой функциональности.
    • Pure MVC
    • Ash framework
    • Reactive Cocoa
    • componentKit

  • Третья глава посвящена тестированию и тому, что позволит вам писать тестируемый код.
    • Expecta
    • OCMock
    • Blood Magic

  • Глава четвертая же посвящена не столько фреймворкам, которые вы можете встроить в ваш код, сколько инструментам, которые могут облегчить вашу работу с кодом.
    • mogenerator
    • fastlane
    • YACC/LEX

Заранее, попрошу у вас прощения. Как говорил Бильбо Бэггинс:

Добрую половину из вас я знаю вдвое хуже, чем следует, а худую половину люблю вдвое меньше, чем надо бы.

Вот и я так же — в данной статье не будет достаточно подробного разбора ни одного из предлагаемых инструментов (здесь даже нет ни одной строчки кода!) — лишь субъективные ощущения и рекомендации по использованию. А инструментов при этом немало и каждый из них заслуживает отдельного разбора.
НО! эти разборы есть — и для каждого инструмента я постарался указать, где можно найти примеры его использования, документацию и связанную информацию — благо, ее действительно более чем достаточно.

Итак, начнем.

Глава 1. Вводная

cocoaPods

Наверное, здесь нет людей, которые бы не слышали об этом инструменте. Инструмент по управлению внешними зависимостями (third-party) для вашего проекта.
Почитать про него можно вот тут. Не буду особенно сильно расписывать здесь про него, так как все всё сами прекрасно знают :-)
А тем кто не знает, можно прочитать тут и принять к сведению, что этот инструмент для iOS разработчика — must have.
Переход к его использованию значительно уменьшает время прототипирования новых приложений, а написание собственных подов существенно дисциплинирует процесс создания реиспользуемого кода.

Достоинства

  • Невероятно легкое управление зависимостями проекта. Добавление новых производится одной лишь строчкой.
  • Возможность упаковывать свой реиспользуемый код в удобные и простые контейнеры, облегчающие его реиспользование.
  • Отделение кода зависимостей от кода вашего приложения — что уменьшает соблазн изменять код third-party инструментария (Иногда это, конечно, бывает полезно и даже необходимо. До первого обновления модуля зависимости) и заставляет организовывать код этих дополнений более аккуратно и «сбоку».

Недостатки

  • Процесс добавления зависимостей облегчен настолько, что теперь нужно аккуратно следить за тем, чтобы в проект не добавлялось слишком много этих самых зависимостей на каждый чих. И если это уместно для прототипирования — то для долгоживущих проектов — это важный вопрос, так как не все third-party идеальны, но за их ошибки перед своими пользователями отвечаешь именно ты.
  • Если ты хочешь чего-то странного, то покопаться с этим придется иногда преизрядно. Например, у меня в какой-то момент заняло существенное время научить проект корректно собирать разные архитектуры для разных таргетов. Возможно это стало проще и комфортнее сейчас, но факт остается фактом.
MagicalRecord

Фреймворк для работы с базами данных, представляющий из себя обертку над CoreData. Прочитать о нем подробнее можно тут.
MagicalRecord поможет вам начать использовать CoreData, даже если до этого вы всячески ее избегали. Кто-то когда-то сравнил использование MagicalRecord без понимания принципов работы CoreData с наслаждением от чашечки кофе в горящем доме — и я могу его понять, но по моему личному опыту — мне гораздо легче оказалось разобраться в тонкостях CoreData просто читая и используя код MR в практических задачах.

Сама по себе MR является адаптацией концепции ActiveRecord для Objective-C. Эта концепция переносит фокус при работе с БД с баз и таблиц на отдельные записи, осуществляя привязку к ним основных операций по работе с базой. То есть, вместо «Таблица — удали эту запись», мы говорим «запись — удались», или «запись — создайся, изменись, дай мне все экземпляры, соответствующие твоей сущности».

Ее использование позволяет значительно сократить количество кода в вашей обертке над БД. А кроме того, позволяет писать значительно более простой и безопасный код для работы с базой.

Достоинства

  • Отлично скрывает от вас многие грабли CoreData, значительно уменьшая код, необходимый для инициализации, сохранения, модификации записей. Упрощает работу с дочерними контекстами.
  • Изначально ориентирована на асинхронную работу с данными, а значит стандартные действия с ней не будут вызывать подвисаний UI.
  • Низкий порог входа — этот фреймворк позволит вам прочувствовать достоинства баз данных, но не проведет при этом через все глубины ада.

Недостатки

  • Как и все асинхронные части кода — ее довольно тяжело покрывать тестами.
  • Кроме того вам, как и в других способах работы с БД, придется на уровне архитектуры гарантировать корректность базы данных — что приложение не будет пытаться писать и читать в одном и том же месте в один момент времени (нет, у нее это вполне получится, но вот результат вам не очень понравится)
  • Она не избавляет вас полностью от написания предикатов, запросов а прочего (Нельзя сказать, что это прямо совсем недостаток, потому что странно было бы ожидать подобного)
PromiseKit

Фреймворк для работы с такой концепцией как Promise. Подробнее про него можно прочитать вот тут.
Если вкратце — это способ организации работы с асинхронным кодом в виде цепочек действий. Зачем это нужно?
1. Асинхронный код становится простым и линейным вне зависимости от того, в каком потоке он исполняется. Перед глазами его строгая структура — что и зачем выполнится. На одном экране можно поместить следующего вида логику: Загрузить два конфига. Когда они докачаются — проверить их на валидность. После этого запустить какую-то анимацию, после этого отобразить результаты.
2. Асинхронный код становится атомарным, инкапсулированным и реиспользуемым. Очень часто появляется много соблазнов разрушить инкапсуляцию асинхронных операций — использовать какие-то общие переменные, например — что делает асинхронный код более сложным для изменений.
3. Появляется комфортная возможность для обработки ошибок всей цепочки исполнения. Каждый кто писал цепочки асинхронного кода сталкивался с тем, что чем длиннее цепочка — тем сложнее корректно обработать ошибки, тем сложнее и более громоздким становится код.

Достоинства

  • Значительно упрощает построение графа исполнения асинхронных операций. Просто сделать, чтобы одна операция началась сразу же за другой или же за несколькими другими
  • Значительно упрощает обработку ошибок, возникающих на разных этапах исполнения цепочки (особенно, в том случае, когда цепочка в приложении имеет смысл только в случае полного успеха всех операций и не имеет смысла во всех остальных)
  • Позволяя грамотно инкапсулировать асинхронные операции — значительно облегчает их множественное использование в разных местах приложения.

Недостатки

  • По прежнему необходимо быть аккуратным с памятью при работе с промисами
  • Нужно некоторое время на то, чтобы привыкнуть работать не с «кодом», а с «оберткой, которая вызовет твой код»
  • Неудобство во время дебага.

Глава 2. Архитектурные фреймворки или что-то около того

PureMVC

Почитать про него подробно можно вот тут.
Наверное, один из первых крупных архитектурных фреймворков, с которым мне пришлось столкнуться во время работы над мобильными приложениями.
Если вкратце, это формальное разбиение MVC паттерна на более мелкие сущности:
1. VO (value Object) — он же привычный из CoreData Entity
2. Proxy — при своем странном названии, он тем не менее представляет из себя Обертку над моделью — отвечают за доступ к данным и нетривиальные геттеры (аггрегаторы) модели
3. Mediator — то, что привычнее считать контроллером в MVC, управляет отображением во View и обрабатывает поступающие из нее сигналы. Может быть подписан на нотификации, посылаемые через фасад
4. View — ну View он и есть View. Код ответственный за визуальное представление информации.
5. Facade — синглтоновская сущность, существующая все время работы приложения. В нем должны быть зарегистрированы при создании все объекты классов Proxy, Mediator и Command
6. Notification — сообщение, посылаемое через фасад. Его получат все, подписанные на нее, медиаторы (и только они)
7. Command — то, что описывает flow приложения — подчиняясь паттерну Command — запускаются, выполняются, завершаются, не удерживаясь в памяти, по сути представляющие из себя нотификацию с выполняемым кодом.

Достоинства

  • фантастический запас прочности архитектуры — в моем примере в код лили все подряд на протяжении несколько лет и разработка при этом не встала и дожила до появления возможности глубинного рефакторинга и появления адекватных техпроцессов.
  • Быстрое прототипирование — архитектура уже неплохо размечена за тебя. И если ты имеешь опыт работы с этой структурой, то можешь довольно быстро, но при этом в модульной и итеративной форме, наращивать функциональность приложения.
  • Очень формальная структура — если уж ты начал ею пользоваться в проекте — от нее довольно тяжело отступить в сторону — что приводит к тому, что все разработчики проекта пишут код единообразно.

Недостатки

  • Предоставляет огромное количество возможностей писать плохой код, за счет того, что все критичные модули, фактически, являются синглтонами (в данном случае, зарегистрированными в синглтон-фасаде), а значит проект очень быстро имеет все шансы обрасти неявными зависимостями
  • Ад дебага. Очень мало явного связывания, соответственно, для того, чтобы перейти по цепочке выполнения надо по строке, которая символизирует сущность, найти место где эта сущность регистрируется, найти проассоциированный с ней класс и перейти к нему. Повторить до готовности. Глубина вложенности стека достигает совершенно космических значений (своими глазами видел сотню)
Ash framework

Почитать про него подробно можно вот тут. Там же можно найти и примеры использования.
Конкретно этот фреймворк больше подходит к разработке игр, но если в какой-то момент вы можете сказать, что ваше приложение перестало быть stateless — то ознакомиться с этой вещью будет как минимум, познавательно.
Если вкратце, это фреймворк реализующий паттерн Entity-Component-System и оперирует следующим набором сущностей:

1. Entity — собственно, некоторая сущность (для простоты описания, я буду использовать примеры из игровой тематики в данном случае, потому что они в большей степени подходят к этому фреймворку). Например — игрок, враг или препятствие. Кроме того сущности могут быть и не столь явными акторами (действующими объектами) — например попап Options, lifeBar, или, например, весь уровень. Оперирует самым минимумом информации, проассоциирована с некоторым набором компонент.
2. Component — некоторый атомарный контейнер с данными. Например — позиция, скорость, разрушаемость,…
3. Filter — Некоторый фильтр для того, чтобы иметь возможность получить не весь набор Entity — а только лишь те, которые его проходят. Представляет из себя набор ключей компонент. Таким образом если фильтр содержит в себе ключи «позиция» и «скорость» — то сущности, которые не двигаются, или неигровые сущности — его не пройдут.
4. System — описание характера изменений сущностей. Оперирует с отфильтрованным набором сущностей и как-то его преобразует. Например система гравитации. Берет сущности, проходящие через фильтр — имеют позицию, скорость, ускорение, подверженность гравитации — и пересчитывает их ускорения с учетом влияния гравитации.
Или MovementSystem — берет все сущности, которые имеют позицию, скорость и ускорение — и меняют скорость на величину ускорения, а позицию, на величину скорости
5. World — некоторый объект, который запрашивает обновление всех систем в определенном порядке.

По сути, этот фреймворк предлагает на поведение объектов с несколько непривычной для классического ООП точки зрения — не объект реагирует на изменения, а мир находит объекты, способные реагировать и меняет их самостоятельно, посредством систем.

Достоинства

  • Заставляет разбивать логику на ортогональные сущности, думать в критериях не мешающих друг другу модулей
  • Легкость изменения характеров (behavior), Легкость добавления новых.
  • Стейт-машина встроена во фреймворк by design. Таким образом можно переключаться между целыми наборами характеров и реакций.

Недостатки

  • Это не серебряная пуля. Стэйт машины довольно быстро набирают сложность и теряется контроль над кодом. Требуется очень четко понимать, чего и сколько в ней должно быть.
  • Ортогональность модулей и полный stateless — очень красивая концепция в голове, на практике же ее поддерживать очень непросто. А любые shared данные вне компонент очень быстро приводят к большому количеству сложностей и возможных багов по типу race condition (хоть и в одном потоке).
Reactive Cocoa

Наверное, один из самых значительных сторонних фреймворков для iOS разработки из существующих ныне. Почитать о нем подробно можно вот тут и на хабре — и я настоятельно рекомендую это сделать. Он предлагает для использования некоторую замену основного паттерна iOS (MVC) на его аналог MVVM и в своем использовании опирается на реактивное программирование.
Если вкратце — Реактивное программирование — парадигма разработки, ориентированная на работу с потоками данных. Если при обычной разработке (императивной), разработчик программирует в стиле «программа должна сделать А, после этого сделать В, после этого сделать С». Если А, В и С — значительные логические операции расположенные в различных модулях приложения — с разрастанием количества таких блоков — увеличивается связность программ, возрастает их сложность и размер.
Самым известным примером такого связывания различных модулей для длительных операций является шаблон делегирования, но он не решает всех проблем.

  • Что, если нам нужно, чтобы завершение этой операции инициировало сразу несколько других?
  • Что, если нам нужно, чтобы в различные момент работы приложения эту операцию могли запускать различные сущности?
  • Код связанный с одной операцией оказывается разбросан по большому количеству модулей и его модификация представляет сложности.
  • Добавление новых операций в цепочку действий (особенно в ее середину) — как правило сопряжено с достаточно большим количеством кода.

Что предлагает Reactive Cocoa и MVVM:

  • Операции объединены в модули, в интерфейсы которых вынесены обертки над исполняемым кодом, так называемые сигналы и комманды.
  • В отличии от классического MVC — основанная на сигналах архитектура позволяет связывать ассоциированные сущности напрямую, без прослоек.

Для себя я считаю ReactiveCocoa развитием идей PromiseKit, расширением их на уровень архитектуры приложения.

Помимо того, что ReactiveCocoa наследует достоинства и недостатки PromiseKit — хотелось бы добавить следующее:
Достоинства

  • Последовательности действий приложения занимают меньше кода, их легче охватить взглядом, а значит легче понимать работу приложения.
  • Легкость манипуляций с последовательностями действий — легко добавить промежуточный шаг, добавить или убрать побочный шаг.

Недостатки

  • Дебаг кода с Reactive Cocoa — очень непрост.
  • Легкость работы с сигналами провоцируют неконтролируемый рост связности приложения и очень легко потерять контроль над его выполнением, когда цепочки становятся длинными и пересекающимися между собой
  • Как и многих других фреймворков такого рода — его лучше избегать в крит.секциях вашего приложения — он не очень быстрый.
componentKit

Недавний подарок от команды разработчиков Facebook, о котором можно подробнее прочитать тут. Не знаю, как вам, но я не один раз с грустью смотрел на HTML и CSS — как способы задачи интерфейсов. То есть на то, что называется декларативным UI.
Если вкратце — это, согласно википедии, способ описания сущностей, сконцентрированных не на «как создать ее», а на «что она из себя будет представлять, будучи создана». ComponentKit представляет из себя развитие идей, появление которых в iOS разработке можно увидеть в Автолэйауте — концепции при которой — изменяя один элемент интерфейса — нет необходимости делать множество операций для того, чтобы весь остальной UI остался выглядеть корректно. Если в старом UIKit изменив размер какого-то UIView — появлялась необходимость пересчитать позиции всех других UIView относительно нее, что иногда приводило к нетривиальному коду работы с координатами, то теперь достаточно сказать, что эта View будет отстоять на 20 пикселей от View, которая выше и на 30 — которая ниже — и как бы мы не изменяли ее размер — это соотношение останется неизменным.
ComponentKit пошел в этом плане дальше — она предлагает достаточно большое количество решений для стандартных сущностей, работа с которыми раньше представляла собой много мороки:

  • Удобная работа с CollectionView/TableView, предполагающее работу не столько через фиксированный dataSource, сколько через наполнение таблицы, подобно обычной работе с массивами. (Вместо «по такому индекс-пасу у нас лежит такая ячейка» — «положить на такое-то место в таблице такую-то ячейку»). Если вам приходилось строить таблицы с неоднородными данными (например построение странички-описания жилья в приложении для аренды недвижимости — где на одной страничке может присутствовать с десяток различных типов ячеек с разным поведением)
  • Стэйты элементов дают возможность описывать сразу несколько характеров, которые может принимать элемент в зависимости от контекста, без необходимости разносить это на разные .xib, разные классы и так далее
  • Удобный адаптивный лэйаут, позволяющий в декларативном стиле описывать коллекции элементов, одинаково подходящих для различных разрешений устройств и ориентации экрана (все это можно сделать и с помощью AutoLayout — но декларативность значительно более гибка)

Достоинства

  • Описание интерфейса в коде, но в лаконичной и адаптивной форме.
  • Продуманные решения для стандартных компонентов

Недостатки

  • Иммутабельность компонентов — если ты хочешь сделать изменчивый интерфейс с большим количеством анимаций, то это не твой выбор.
  • Новизна фреймворка — недостаток, конечно, относительный, но проблемы при его использовании вполне могут возникать.

Глава 3. Тестирование.

Про тестирование говорится много и разного. Хорошего и плохого. Кто-то говорит, что тестирование является прямо таки серебряной пулей, кто-то, что наоборот, оно отнимает время, но на мой взгляд — это является одним из ключевых этапов в развитии программистов — и прежде чем об этом судить — через это обязательно необходимо пройти.
Я бы еще рекомендовал прочитать вот эту недавнюю статью — в ней предлагается поверхностный, но приятный обзор того, какой инструментарий в этом плане доступен iOS разработчику.
Если говорить серьезно — TDD — это, пожалуй, наиболее сильно повлиявшая на меня, как на разработчика, концепция. И дело не в том, что автотесты позволяют избежать регрессии, позволяют писать код быстрее, меньше переживать из-за вносимых изменений. Основная польза автотестов заключается в том, что не каждый код возможно протестировать. Именно, благодаря тестированию, обретают смысл такие аббревиатуры, как DRY и SOLID.

Expecta

Почитать про него подробно можно на страничке его репозитория, а так же его родительского репозитория. Там же лежит достаточное количество примеров его использования — достаточно для того, чтобы начать им пользоваться.

Если в вашем коде:
1. Присутствует много неявных зависимостей (например, синглтонов, которые вы дергаете по поводу и без) — этот код невероятно сложно тестировать, потому что вам нужно удовлетворить все эти зависимости. А с точки зрения развития приложения — изменение в коде этих самых неявных зависимостей повлияет на неизвестное количество мест в коде неизвестным образом. Однозначно плохо.
2. Если в вашем коде есть больше количество не stateless связей — этот код сложно тестировать, потому что вам нужно тестировать целые связки модулей во всех возможных сочетаниях их стэйтов (то есть количество тестовых кейсов растет экспоненциально). А с точки зрения развития приложения — в каждом из сочетаний состояний может быть какой-то баг, а еще очень высоки риски того, что связанный модуль окажется не в том состоянии, которое мы от него ожидаем. И все дополнения в коде имеют весьма и весьма высокую цену.
3. Если ваш код не DRY — то вам придется писать тесты для каждого из повторяющихся кусков кода, что увеличивает объем работы. А с точки зрения развития приложения — сложность поддержания работоспособности повторяющегося кода пропорционально количеству повторов.

И многое, многое другое.
Таким образом оказывается, что тестируемый код сам по себе более поддерживаемый, более простой сам по себе, более простой при внесении изменений. И вообще сам по себе экономит прорву времени.
А лучший способ проверить то, что ваш код — тестируем — это покрыть его тестами. Если я правильно понимаю, со временем необходимость в большинстве тестов отпадает, потому что вы уже по выработанной привычке пишете тестируемый код, но до того времени надо еще и дойти.

Так вот, связка Specta/Expecta представляет собой фреймворк для написания тестов в стиле BDD.

Достоинства

  • Сам подход BDD хорош тем, что у вас в одном источнике хранится как тестирующий код, так и спецификация, которой соответствует тестирование. На основе нее можно генерировать понятные не только IT-специалистам документы, которые могут обсуждаться как с QA специалистами, так и с заказчиками со стороны бизнеса.
  • Фреймворк простой и изящный, что позволяет оставлять тесты легко читаемыми и уменьшить риски поддержки тестов (становится не так нужно тестировать ваши тесты)

Недостатки

  • На самом деле — при тестировании реальных приложений лежат грабли на граблях — и через все эти грабли нужно пройти. Тестирование асинхронных баз данных, тестирование UI и вообще тестирование чего-либо асинхронного требует серьезного осмысления и продумывания.
  • Небольшой и, я надеюсь, временный недостаток — последнее время нестабильно ведет себя интеграция с XCode этого инструмента
OCMock

Почитать про него подробно можно тут.
К сожалению при тестировании не всегда есть возможность полностью исключить внешние и неявные зависимости модуля без значительного усложнения кода. И тут на помощь приходит OCMock — он позволяет эмулировать поведение внешних объектов (в том числе и системных фреймворков), изолировав ваш модуль от всех внешних воздействий (скорости работы сетевого соединения, неожиданных ошибок) и позволяет работать с гарантированно чистым контекстом (сервер вернет то, что нам нужно, вне зависимости от того, что по жизненному циклу приложения, мы не можем ожидать такого ответа, в NSUserDefaults по нужным ключам лежит то, что нужно для конкретного теста, а не то, что там оставили предыдущие тесты или бог знает что еще).

Данный инструмент довольно быстро приучает мыслить в критериях «песочницы» и позволяет в следовании принципам SOLID не скатываться совсем уж в Enterprise стиль кодирования — который из-за своей многословности чрезвычайно медлителен в разработке. Он позволяет самому выбирать где должна проходить грань в выборе между «абсолютно чистым кодом» и «простым кодом».

Достоинства

  • Существенно облегчает процесс тестирования модулей со внешними зависимостями
Blood Magic

Фреймворк, разработанный одним из читателей — 1101_debian, почитать подробнее можно тут.
Пожалуй, я бы хотел упомянуть этот фреймворк рядом с темой про тестирование потому, что он помогает в ответе на вопрос КАК писать тестируемый код.
В этой статье я уже неоднократно писал о такой страшной штуке, как неявные зависимости. И вторым способом от них избавляться (первым является вдумчивый взгляд на код с целью понять, какие из зависимостей могут быть вообще вынесены из разрабатываемого модуля. Как ни странно — чертовски эффективный способ) является Dependency injection — иными словами все неявные зависимости необходимо сделать явными.

Если вкратце — то хорошей практикой в том, чтобы сделать модуль действительно независимым, тестируемым и, что не менее важно — реиспользуемым — является избавление от неявных зависимостей. Иными словами, .h файла должно быть достаточно для того, чтобы полностью понять что именно использует этот модуль для работы. Во-первых это значительно облегчает тестирование объекта (мы гарантированно знаем список модулей, необходимых для того, чтобы модуль работал и можем все их мокировать посредством OCMock или любым иным доступным способом). Во-вторых, это дает нам лишний повод задуматься над тем «а не слишком ли много знает о выполняемом коде этот класс? Может быть его разбить на несколько? А может есть какой-то способ логически сгруппировать эти зависимости в отдельные модули?».

Достоинства

  • Являясь некоторым дополнением к языку, позволяет избежать некоторого количества бойлер-кода. Например, ленивое инстанцирование зависимости делается в одно слово, а не в метод из 4-5 строк.
  • Позволяет управлять зависимостями на некотором мета-уровне — подменяя зависимость сразу в нескольких, пользующихся ею, классах.

Глава 4. Внешние инструменты

mogenerator

Простой, но чрезвычайно полезный в повседневной разработке инструмент, облегчающий связь абстрактной модели базы данных с ее репрезентацией в коде. Почитать про нее подробнее можно вот тут. Если вкратце, он помогает в организации кода работы с моделью таким образом, чтобы обновление модели было наиболее простым и безболезненным — чтобы перегенерация модели не затрагивала кастомной логики.
В чем я вижу для себя фундаментальную пользу этого инструмента — так это в том, что он стал первым инструментом для меня, который достаточно легко позволяет открыть для себя огромный мир кода вне runtime, что есть тоже, на мой взгляд, очень важный этап в развитии программиста:
Задачи должны решаться предназначенными для этого инструментами. Не для всех задач Objective-C наиболее удобный язык и не для всех задач — Runtime — подходящее время исполнения. А кроме того, знания должны быть представлены в проекте в единственном экземпляре (DRY в широком смысле, не только то, что касается непосредственно кода)
Зачем руками следить за соответствием модели и ее репрезентации, если можно это сделать частью одного процесса?
Зачем наполнять базу в рантайме — если можно сгенерировать ее перед деплоем?
Зачем иметь несколько экземпляров документации по раздельности — если можно генерировать ее из комментариев в коде?
Для интеграционного тестирования работы с внешним сервисом, вполне можно подготовить контекст посредством скриптов (создать тестовых пользователей, заполнить их профили, ...)

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

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

Достоинства

  • Становится гораздо комфортнее модифицировать модель .xcdatamodel
  • Инструмент хорошо конфигурируем, что позволяет влиять на генерируемый код, его размещение в проекте. При этом для того, чтобы начать использование нужен самый минимум настроек
fastlane

Подробнее про него можно и нужно прочитать вот тут.
Если вкратце, то это набор инструментов, созданный для того, чтобы максимально облегчить continuous delivery ваших проектов.
Он позволяет вам:

  • Собрать ваше приложение
  • Изменить настройки приложения для сборки
  • Сделать скриншоты
  • Запустить тесты
  • Отправить приложение в стор
  • Отправлять в удобном виде нотификации обо всем этом

и многое многое другое

Кроме того, этот инструмент, как и предыдущий помогает войти в мир внешнего инструментария, задуматься об использовании скриптовых языков (AppleScript, Bash, Python, Ruby) в помощь работе над проектом. Для меня это был очень долгий процесс, поэтому каждую ступеньку в нем я считаю очень полезной и важной. Кроме того, этот инструмент поднимает такую интересную тему как DSL. Тема очень сложная, поэтому прежде чем создавать свои DSL (о чем я вкратце напишу в последней секции этой статьи), крайне рекомендую находить удачные примеры применения их, для того чтобы прочувствовать их смысл. FastLane — один из крайне показательных примеров того, почему DSL — это очень и очень здорово.

Достоинства

  • Простой, удобный и функциональный.
  • Интегрируется и известными серверами Continous delivery (такими как Jenkins, например)
  • Позволяет писать довольно сложные сценарий, которые деплоят, тестируют, подготавливают ваше приложение. И писать об этом вам (например в Slack)

Недостатки

  • Не обнаружено. Действительно удобный инструментарий
YACC/LEX

И, наконец, эта восхитительная пара, тяжелая артиллерия в мире DSL и один из самых мощных инструментов для создания собственных языков. Почитать о ней можно много где, но в частности, есть пара статей на хабре:

Рекомендую с ними ознакомиться, потому что инструмент действительно сложный, но обладает потрясающей областью применимости:

  • Вы можете очень комфортно упаковывать и сериализовать сложные логические сущности — например, мне приходилось писать DSL для описания стейт-машин, которые в форму JSON/XML/… ну никак не укладывались.
  • Вы можете редуцировать полный язык программирования до крайне легковесного, с той целью, чтобы задачи можно было переложить на плечи людей, не знакомых с полноценным программированием (написание тест-кейсов для автотестов, написание конфигов уровней и логики врагов для гейм-дизайнеров, написание бизнес-правил для бизнес-процессов.)
  • Вы можете использовать совсем легковесные DSL для описания каких-то маленьких (но присутствующих в большом количестве) процессов в работе вашего приложения. (Для этого не всегда даже нужны YACC/LEX — хватает и ObjC и Swift или Ruby (Писать DSL на свифте — вообще праздник, радость и удовольствие))

В общем спектр задач, для которых это подходит — огромен.

Достоинства

  • Очень удобная форма описания семантики языка.
  • Очень высокая скорость исполнения в рантайме (из нотации языка генерируется C код парсера, который генерирует очень лаконичное AST)
  • Очень широкая область применения — было бы желание, на этом языке можно писать и полноценные интерпретаторы полноценных языков программирования

Недостатки

  • Крайне высокий порог входа — чтобы написать что-то существенное, нужно крепко поломать мозг и в первый раз это займет по-настоящему немало времени.
  • Если вы используете эту вещь в Runtime — то менеджмент памяти ложится на вас со всей тяжестью.

Вместо заключения.

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

Автор: i_user

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js