Pull to refresh

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

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

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



Данная статья, на мой взгляд, будет достаточно сложна для разработчиков уровня 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 — то менеджмент памяти ложится на вас со всей тяжестью.


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


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

Articles