Skip to main content

Модульность, как серебряная пуля, или best practices на практике

Есть такие штуки в программировании, которые я для себя называю best practices. Сюда я отношу архитектурный фреймворк (Mate), высокоуровневые средство сборки проекта (Maven), модульные тесты (FlexUnit4).

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

В идеале эти вещи нужно рассматривать с прагматичных позиций, оценивая затраты на изучение и внедрение, и предполагаемую пользу. Но это осложняется тем, что вокруг оных best practices очень много идеологии. Типичный предмет религиозных споров -- юнит-тесты. Объяснять, я думаю, не нужно :)

Одиночный разработчик или маленькая плотная команда вполне могут успешно работать без best practices. Тут обычно проекты маленькие и средние, и они целиком помещаются в голове разработчика или тимлида. Сложности взаимодействия между членами команды невелики.

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

Проблема реюзабельности кода

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

Стыдно признаться, но очень часто мы использовали метод копипасты -- копируется код их одного проекта, вставляется в другой и до/пере-писывается. Очевидно, что поддерживать такой код невозможно. А когда он пройдет через три-четыре проекта, то он уже состоит наполовину из шаблонной копипасты, и наполовину из малопонятных хаков.

А ведь начинается все хорошо -- проектируется компонент, организуется инкапсуляция на уровне классов и пакета, готовится API. В какой-то момент компоненту нехватает функциональности, она дописывается, и совершенно случайно цепляется зависимость от специфичного для конкретного проекта объекта. Этот момент может оказаться незамеченным, но уже все, компонент пропал :( Через N недель ты пробуешь использовать его в другом проекте и видишь, что он через зависимости тянет половину предыдущего проекта.

Низкая связанность

Ну так что же делать? Мы все знаем, что делать -- писать низко-связанный и жестко-сцепленный код. Внимательно контролировать зависимости между объектами. Ну так пишем, контролируем, но не получается :)

В какой-то момент я узнал о концепции IoC. Вернее, не узнал, а понял, зачем она нужна. Знал-то я и раньше, только не понимал :)

Итак, зависимости между объектами -- сложная штука. Контролировать это вручную мало реально, легко ошибиться и не заметить этого. Лучше доверить это дело хорошему архитектурному фреймворку, поддерживающему концепцию IoC.

И так у нас появился Mate. Насчет низкой связанности все стало гораздо лучше. Но не совсем хорошо :)

Автономно компилирующийся модуль

Ну вот, зависимостями между объектами управляет фреймворк. Гарантирует ли это отсутствие лишних связей? Нет. А как нам добиться гарантии, что наш компонент действительно автономен? Как нам быть уверенными, что он легко интегрируется в новый проект, причем как есть, без всяких модификаций?

Для этого компонент нужно компилировать изолированно от остального проекта. То есть, сделать модуль -- swc библиотеку. Мы храним сорцы отдельно от проекта, компилируем их отдельно, и это дает 100% гарантию, что у модуля нет связей с специфичными классами проекта.

Теперь приходится больше думать над правильным API модуля и это благотворно сказывается на всей архитектуре проекта.

Раньше проект был монолитом с одной папкой src, где лежали все классы. Теперь проект -- куча отдельных модулей. Хороший побочный эффект -- ускорилась компиляция. Нехороший побочный эффект -- сложнее настроить проект в IDE. Для каждого модуля нужно задать настройки компиляции и указать зависимости от других модулей. Поднять проект с нуля на рабочей машине становится проблемой.

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

Maven и flexmojos -- большая тема. Чтобы хорошо об этом рассказать, нужно несколько статей. И я буду об этом рассказывать, но не сейчас :)

Автономно тестирующийся модуль

Хорошо, модуль у нас компилируется изолированно. Но ведь его не только компилировать, его разрабатывать нужно :) Писать код, компилировать, запускать, наблюдать, тестировать и т.д. А как мы будем запускать и дебажить swc файл?

Сначала в голову приходит мысль -- сделать приложение-оболочку, которое будет компилироваться в swf и использовать API модуля. Вариант неплохой, но не всегда удобный.

Есть и другой вариант -- юнит-тесты :) Вот тут-то, на автономных модулях видно их удобство. Они легко пишутся, легко запускаются, и хорошо контролируют API модуля. Это в теории, как будет на практике -- поглядим.

В общем, по первому варианту, или по второму, или по двум вариантам сразу модуль можно разрабатывать и отлаживать.

PROFIT?

Ну посмотрим, через N месяцев будет видно :)

No votes yet