Archive for the ‘Код и программистское’ Category

Теория планного программирования.

Понедельник, Август 10th, 2009

Было как-то дело, что я обчитался книжкой по Аспектноориентированному программированию, проникся и понял как это круто. Увы, похоже в ближайшие сто лет попробовать мне это на практике не удастся.

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

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

Или например, у нас в целом модуле BankTransaction во всех интерфейсных функциях мы должны проверять, что клиент имеет право проводить транзакции. Аналогично, единственный метод — в начале каждого метода делать проверку.

Так вот, как раз логирование или проверка прав и есть аспект. То есть, какая-то функциональность у которой одна и та же идея, но она размазана по куче кода.

И в целом, если объектоориентированное программирование крутится вокруг идеи выделения объекта и методов, чего можно делать с объектом. То аспектноориентированое программирование крутиться вокруг выделения идеи аспекта и собирания его из размазанного вида в одно место. Так, что вместо бегания по 100 функциям разных объектов и copy/paste одного  того же куска, мы можем сказать — делать то-то при входе в функции таких-то классов.

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

И вот, что я подумал. А ведь это чем-то похоже на объявление переменной объекта. Когда мы ее объявляем, то автоматически деструктор вклинивается в то место, где переменная выходит из области видимости.

Да, поясню слово вклиниваться. Я имею в виду, то, что мы не пишем никакого кода в определенном месте программы, но тем не менее есть код (на самом деле компилятор), который «знает», что в этом месте надо выполниться.

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

Но, это все так, отклонения от общей темы.

Дальше моя мысль прыгнула в следующем направление. Если вдуматься, все это развитие языков от низкоуровневых к процедурным, от процедурных к объектным, от объектным к аспектным двигалась в достаточно простом направление —  упростить работу программиста и свести воедино то, что было раньше разбросано по разным местам.Сначала копии кода сводились в процедуры, потом куча процедур работающих с похожими данным сводились в объекты. Объекты которые работают похожим образом сводились в иерархию объектов. Объекты работающие похожим образом, но с разными типами — в templat’ы. Естественно — это не единственная особенность развития языков программирования, но по крайне мере оно четко прослеживаемая.

Так, вот, возвращаясь к планному программированию. Моя идея следующая — херим все сущности, которые были введены раньше (функции, объекты, аспекты, модули, namespac’ы, templat’ы и т.п.) и вместо всего оставляем совмещая все это получаем коцепцию плана.

Итак, что такое план?

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

Основная идея, это построить их так, чтобы сделать код гораздо более податливым. Если сейчас код находится в классе X, похожий код в классе Y и Z, то начинается целая свистопляска, чтобы его объединить, или хотя бы свести в одно место (как с логированием).

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

Чтобы не углубиться окончательно в теоретические изъяснения, приведу пример. Хотим мы написать адресную  книгу. Как мы это делаем?

— Начинаем с того, что для любой программы есть стандартный пустой план — application, который содержит все остальные планы.

— В этот application план, мы добавляем UI план, в который добавляем MainWindow план. И например мы вписываем код, который читает файл, показывает табличку с именами на экране, а так же делаем чтобы можно было редактировать и сохранять файл.

— Мы обнаружили, что наш MainWindow план стал слишком жирным, выделяем Storage план, переносим туда весь код по работе с файлом (чтение, запись, временное сохранение измененных записей).

Пока звучит, точно как объектноориетированное, а то и процедурное? Согласен, но движемся дальше.

— Мы решаем, что нам надо добавить 5 полей. Начинаем их добавлять, и обнаруживаем, что мы нам нужно будет тупо copy-paste работу с полями для того, чтобы их отрисовывать в  MainWindow и сохранять, считывать в Storage. И вот тут наступает самое интересное, мы вводим новый план ContactField в который составляем из куска Storage по работе с полем и куска MainWindow по работе с полем.

Замечу, что ContactFiled содежит не копию кода, а зеркало кода. То есть, редактируя в ContactFiled оно редактируется в MainWindow или Storage. Итак, это первая выгода, что мы теперь можем просматривать код в том виде, который нас действительно интересует — либо с точки зрения UI (смотрим в MainWindow) или Storage или с точки зрения Field. На самом деле, теперь мы можем в этот план включать любые другие места где наше поле используется, передается, храниться и обрабатывается. То есть, всегда есть место где централизованно можно  видеть сущность.

— Движемся дальше. Так как мы знаем, чтона самом деле полей будет много, то план ContactField мы переименовываем в ContactFieldList и указываем, что туда должны актоматически вклиниваться все планы с названием ContactFieldList.ContactField*. Теперь внутри ContactFieldList мы можем делать новые планы, которые будут заниматься сохранием и отрисовкой и которым автоматически будут вклиниваться. Вот тут, наступает вторая выгода. Выгода состоит в возможности работы с планами в докомпиляционное время. То есть, то, что можно указать что все планы с таким-то именем должны вклиниваться. Современные языки предоставляют достаточно небольшое количество докомпиляонных средств.

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

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

Самое важное, что я осознал, во время написания статьи, фактически планное программирование — это две вещи:

а) Мощная среда разработки, которая умеет делать много разных отображений кода и работы с этими отображениями.

б) Уменьшение количество сущностей (класс, процедуры, шаблоны, интерфейсы) и сведения их к минимальному количеству.

Надеюсь, кого-нибудь зацепила эта писанина. 🙂 Жду, комментариев, критики и горячих споров.

P.S. Насчет Mix-in и Closure. И то и другое продолжает решать проблему дублирования кода, но все таки не так радикально как предложено у меня 🙂

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


Бранчинг.

Среда, Июль 22nd, 2009

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

Кстати, не могу найти нормальный перевод слову бранчится. Как действие слово «ответвляем» меня вполне удовлетворяет, а вот применительно к такому предложению «как стоит ответвляться/ветвится» звучит странно…. Мдя…Будь же ты, проклят герундий.

Ладно, назад к теме. Увы, серебряной пули среди стратегий нет. Так что, пробежимся по стратегиям.

Кстати, сразу напишу, что идет речь о том как бранчиться для создания постоянных (а не временных) веток. То есть, я не затрагиваю вопрос о том, что если мы делаем какие-то крупные и длинные изменения, что стоит их делать в отдельной ветке, которая будет удалена по окончанию работы.

— Простейшая стратегия

Если есть один человека, один продукт и одна версия то можно вообще не бранчиться. Все делается в trunk’е, ну разве что, можно помечать стабильные билды и релизовые билды.

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

— Стратегия разумного минимума

Бранчимся только тогда, когда это уже абсолютно необходимо. То есть, как в прошлом методе ведем все в trunk, если надо сделать hotfix, то ответвляем от версии, которая отдана заказчику и там делаем исправления и мержим назад в trunk все исправления.

— Стратегия обычной средней фирмы

Естественно в какой-то момент, становится понятно, что для любой выпущенной версии нужно будет сто процентов делать хоть какие-то исправления, так что можно сразу нарезать бранчи для каждого релиза наперед. Да и плюс, людей прилично, кто-то работает над исправлением для предыдущей версии, кто-то работает над новой функциональность в trunk’е.

Вот тут уже начинаются расхождения.

а) Ранний бранчинг

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

б) Поздний бранчинг

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

Кстати, на этом размере появляется еще один вопрос в перпендикулярной  плоскости. Как организовывать структуру веток. Можно так

/Product1/trunk

/Product1/branch1

/Product1/branch2

/Product2/trunk

/Product2/branch1

а можно

/trunk/Product1

/trunk/Product2

/branch1/Product1

/branch1/Product2

Мне по душе больше первый метод. Я считаю, что продукты должны быть по возможности независимые друг от друга и поэтому должна быть возможность отдельно их выпустить, а вывод должна быть возможность отдельно бранчить Product1 и отдельно Product2.

Если же они насколько связаны, что не имеет смысла их выпускать отдельно, то скорее у вас не два продукта, а один продукт с несколькими модулями.

Кстати, именно второй вариант (где бранчат все продукты вместе) для меня был не то, чтобы новостью, но я раньше с ним не сталкивался.

Ну и последняя добавка. Опять же с любой веткой для релиза можно поступать теме же описанными стратегиями.

— Стратегия крупной фирмы

А вот тут собственно говоря наступает самое интересное, то о чем я прочел.

Предположим, что у нас есть большое количество веток в которых мы ведем разработку. Например для версий 5.5, 6.4 и 6.5 и 7.0.  Ну и еще параллельно ведется пару долгоиграющих проектов, который затрагивают несколько продуктов. Что мы хотим — иметь возможность перетягивать исправления. Условно говоря фиксы из 5.5, чтобы можно было применить в 6.4, 6.5, 7..0 и долгоиграющих проектах.

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

Собственно откуда берутся приключения? Первое, мы плохо можем знать другую ветку. Человек работающий над 5.5 достаточно плохо осведомлен о специальном проекте, который собран из частей 6.5 и 7.0. Получается если он закидывает изменения — то он не знает того когда. Если же люди работающие на этим специальным проектом пытаются вытянуть изменения из 5.5, то они тоже могут не слишком хорошо знать код.

В таком случае весь мержинг делают через ветки от которых были порождены текущие. То есть, чтобы из 5.5 перенести в 6.5 то из 5.5 мержат в 5.0, из 5.0 мержат в trunk, trunk мержат в 6.0, 6.0 мержат в 6.5.

С одной стороны, количество мержингов становится ГОРАЗДО больше с другой стороны, мержиться с веткой от которой была порождена текущая всегда гораздо проще и гораздо больше шансов, что нам знакомы обе ветки.

Далее, мержинг проводится в тот момент, когда ветка стабильна (идеально в релизовом состоянии). И еще, как только от ветки что-то бранчится, то после этого в этой ветке не ведется никакой разработки. Условно говоря, как только сделан branch 2.0, то в trunk не кладется никакой код кроме мержей. Таким образом в trunk мержится только стабильный код и поэтому trunk остается в достаточно стабильном виде.

Что-то я замудро описал. В целом, разработки делаются только в «листах» дерева (веток от которых не было ничего ответвлено) и для переноса изменений, мы идем по дереву вверх и вниз. Такой метод сложен, но зато позволяется все НЕ листы ветки держать в стабильном состоянии. И это снижает вероятность проблем при следующих мержах.

Вот собственно эта схема для меня и была новая.

UAC, тудыть его в качель.

Четверг, Июль 16th, 2009

Товарищи, никто не подскажет, где находится очередь желающих оторвать достоинства тому, кто придумал UAC для Vista? Что? Достоинств на всех не хватит? Я предлагаю тогда их пришывать назад и отрывать по второму кругу.

А если серьезно. Никто  случайно не сталкивался с следующей проблемой

— Есть MSI

— Устанавливаем его на машину, выбирая галочку «для всех пользователей»

— Он благополучно устанавливается, по ходу поднимая UAC и прося подтвердить что я таки хочу его поставить (я Admin, поэтому он хоть credentials не просит)

— После того как установилось, щелкаем снова по MSI — он запускается в Maintanance mode (опции modify/repair/remove). Чтобы я не выбирал (modify, repair или remove) он в результате посылает нафиг по тому поводу что у меня нету IE 5.5 или выше (на самом деле есть).

Собственно грабли заключаются в том, что почему-то в этом maintanance mode Vista не выплевывает UAC окошко и не elevate процесс. В результате MSI пытается что-то там делать но Windows бьет его рукам и говорит «не скажу тебе противный, какой у меня IE», ну и MSI естественно передает мне теже слова.

Подтверждением того, что это таки проблемы с elevation, является то, что я насильно (Run as adimistrator) запустил MSI сразу elevated и все зашуршало.

А еще забавность в том, что если я делаю теже самые действия только устанавливаю не для «all user on this computer», а только для себя любимого, то проблемы тоже исчезают и оно таки показывает UAC для maintanance.

Да, кстати, для полноты картины. Мне нужно починить это не с точки зрения пользователя, а именно с точки зрения программиста. То есть, что-то сделать с исходниками (они у меня есть) MSI (здравствуй Wise Installer Studio), чтобы эта фигня зафурычила.

О… и если можно запишите меня в очередь на отрывание достоинств и к MSI’шикам. Хотя нет, пожалуй это перебор… Для них вполне будет достаточно просто мощной затрещины, чтобы в следующий раз делали продукт по проще для понимания.

Космические архитекторы.

Понедельник, Июнь 22nd, 2009

Очень у меня смешанное отношение к архитекторам (естественно софтверным).

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

Но, вот, что меня убивает на корню, когда архитектор простейшую операцию а-ля «почистить зубы» начинают обсмаковывать. А ведь можно почистить не только зубы, да и зубы можно не только почистить но и прополоскать. Итого, давайте введет объект действия и тип действия. Да, а ведь еще есть и цель действия. Тоже, для обобщения введем ее туда. И все эти действия объект может делать над субъектом. Итого, получаем эдак 3-4 разных сущности, которые посылают друг другу сообщения. Ну и само собой, все это может быть распределенным, если например дантист чистит чужую вставную челюсть, так что все это построим на базе SOA.

Почему космические архитекторы? Да потому, что если обычные архитекторы смотрят сверху вниз на код, с высоты многоэтажки, то эти настолько абстрагируются от всего, что уходят в какие-то занебесные космические области, по абстрагированию.