Showing posts with label Programming. Show all posts
Showing posts with label Programming. Show all posts

Friday, April 20, 2007

Aspect-oriented programming

Если что-то появляется, значит, оно кому-то нужно.

И снова новая методология программирования и к ней новый подход к разработке ПО. Приглашаем любить и жаловать: Aspect-Oriented programming (AOP) и Aspect-Oriented software development (AOSD).

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

История:

Aspect-Oriented подход разработан в Xerox PARC в 2001 году под руководством Gregor Kiczales. Этой же группой была разработана первая реализация – AspectJ, которая на текущий момент входит в Eclipse Foundation. Кроме AspectJ уже создано несколько десятков реализаций AOP парадигмы, практически для всех языков.

Идея AOP захватила сообщество - существует Aspect-Oriented Software Association (AOSA), проходят ежегодные конференции по AOSD, в которых в качестве спонсоров выступают Microsoft, Google и IBM.

Основная цель, которую себе поставил Gregor Kiczales, и в результате движения к которой появился AOP – уменьшение кол-ва ошибок и улучшение качества кода за счет борьбы с запутанностью кода и разбросанностью функционала.

Дисклэймер:

Я, человек привыкший думать объектно-ориентированно, не совсем поняла их претензии к OOP. Т.е., на мой взгляд, при правильном дизайне системы (в объектно-ориентированной парадигме) нижеизложенные проблемы не дают серьезного ухудшения качества кода. Описание же AOP, на мой взгляд, слишком похоже на патерны. Но то, что я не вижу преимуществ – это не значит что их нет. OOP тоже плохо приживался в процедурно мыслящей среде.

С чем боремся:

В объектно-ориентированном подходе как на уровне системы в целом, так и на уровне отдельных классов присутствует сквозная функциональность (т.е. та, которая реализована понемногу в разных модулях), а так же большое количество кода, который фактически не относится к функционалу системы. На уровне системы это идентификация, защита, логирование, репликации, сериализация/десериализация, сохранение настроек системы и т.д., т.е. или общий или «сервисный» функционал, зачастую реализованный в разных частях системы (примеры: сериализует один модуль - десериализует другой; каждый из модулей системы занимается идентификациеи и защитой), в связи с чем усложняется отладка и поддержка (со временем начинается «расползание»). То же самое на уровне классов – класс инкапсулирует в себя не только функциональную часть, но и внутренние «сервисы», обеспечивающие корректность работы – отлов ошибок, работа с памятью и т.д., которые загромождают код и усложняют чтение логики – в следствии чего, опять же сложности с отладкой и длительной поддержкой.

Метод борьбы:

Выделение сквозной функциональности в отдельные модули, которые называются аспектам (aspects). Поведение аспекта в конкретном месте программы (точке подсоединения, join point) определяет advice (собственно программная реализация модуля). Все точки подсоединения конкретного аспекта описываются в наборе pointcut.

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

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

Ссылки по теме:

Gregor Kiczales о AOP (Google Video)

Aspect-Oriented programming на wiki

Книги:

Aspect-Oriented Analysis and Design: The Theme Approach. Siobhàn Clarke, Elisa Baniassad.

Aspect-Oriented Software Development. Robert E. Filman, Tzilla Elrad, Siobhan Clarke, Mehmet Aksit.

Aspect-Oriented Software Development with Use Cases. Ivar Jacobson, Pan-Wei Ng.

Aspect Oriented Refactoring. Ramnivas Laddad.

AspectJ in Action: Practical Aspect-Oriented Programming. Ramnivas Laddad.

Sunday, April 08, 2007

NSIS & Vista

Для того чтобы установить программу в “Program Files” пользователю нужны административные права. Это не новость. А вот новость, появившаяся в Висте – запустить инсталлятор можно только под пользователем уровня доступа администратора машины.

С версии 2.21 в NSIS-е появилась команда RequestExecutionLevel, которая указывает какими правами должен обладать пользователь для запуска NSIS инсталлятора. RequestExecutionLevel может указывать 3 уровня прав: user, admin, highest. Если программа по умолчанию устанавливается в “Program Files”, то должен быть указан уровень admin. Если Команда не используется, то NSIS инсталлятор автоматически определяется Вистой и при запуске требует административных прав, однако после завершения инсталляции выдает сообщение о том что возможно установка программы прошла некорректно.

Итак, если текущий пользователь не имеет административных прав Виста запросит ввести пароль для пользователя, имеющего такие права, и NSIS инсталлятор запустится под администраторским пользователем. В результате чего появляются следующие проблемы:

Все глобальные переменные системы (%UserProfile%, %LocalAppData% и др.) определяются не для фактически залогиненного акаунта, а для администраторского, под которым запущен инсталлятор. Та же ситуация с переменными NSIS скриптов (например $SMPROGRAMS) и доступом к ветке реестра HKEY_CURRENT_USER.

Такая ситуация с переменными выливается в то, что для обычного пользователя через инсталлятор нельзя проставить даже элементарные линки на программу в “Start Programs”, на “Desktop” и в “Launch Menu”. Для того, чтобы пользователь сразу после инсталляции увидел иконку на рабочем столе ставить ее (и все остальные линки) надо для All Users (команда SetShellVarContext all). Это значит, что фактически программу надо ставить не для конкретного пользователя, а для всех существующих пользователей машины сразу.

И, не смотря на то что здесь в FAQNSIS сказано, что для успешного добавления линков в “Start Programs” при инсталляции и для последующего успешного их удаления при анинсталляции надо указать или “RequestExecutionLevel admin” или “SetShellVarContext all”, на самом деле обе операции должны применяться одновременно:

--

OutFile vista.exe
Name Vista

RequestExecutionLevel admin

Section
  SetShellVarContext all
  CreateShortcut "$SMPROGRAMS\Vista Test\hello.lnk" $WINDIR\notepad.exe
  WriteUninstaller $EXEDIR\uninst.exe
SectionEnd

Section uninstall
  SetShellVarContext all
  Delete "$SMPROGRAMS\Vista Test\hello.lnk"
  RMDir "$SMPROGRAMS\Vista Test"
SectionEnd

--

Wednesday, February 28, 2007

Объектно-ориентированному дизайну пора потесниться

Явно пришло время изменений. Предыдущий пост был про идею новой методологи разработки, а сегодня пришло время пересмотреть основы программирования и дизайна – OOP и OOD.

Большинство изменений происходят постепенно. За текучкой мы эти изменения не замечаем.

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

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

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

Самое неприятное - когда красиво спроектированную архитектуру приходится портить ради тестов. Какой выход из этой ситуации?

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

Roy Osherove предлагает кардинально другое решение. Он предлагает поменять подход к дизайну и делать его не только объектно-ориентированным, но и тесто-ориентированным. Итак, на замену OOD идет OOTD (Object Oriented Testable Design).

На мой взгляд идея хорошая. Признание факта противоречия классических канонов OOD и тестирования – это хорошая причина уделить внимание проблемам тестирования еще на этапе проектирования.

Friday, January 26, 2007

ProgressBar и индикаторы выполнения процесса

Любое приложение, содержащее длинные операции (в которых время выполнения может превышать 1 минуту), должно иметь ProgressBar. Причем не просто "бегунок" который показывает что процесс идет, но по которому не понятно сколько выполнилось и сколько еще осталось и вообще работает приложение или "подвисло", а хороший ProgressBar с индикатором % выполнения операции и, желательно, показом сколько времени осталось до окончания выполнения. Такие длинные операции содержатся в большинстве систем работающих с данными и в вычислительных задачах. И, я думаю, большинство разработчиков постоянно сталкивается с ProgressBar-ами.

Что должен содержать ProgressBar и индикаторы процесса выполнения (в логическом порядке, а не в порядке важности):

  • 1. Количественный показатель (в штуках, в байтах и т.д.) общего объема данных, которые требуется обработать.
  • 2. Количественный показатель обработанных данных.
  • 3. Количественный показатель данных, которые осталось обработать.
  • 4. Время, потраченное на обработку данных (из п.2).
  • 5. Ориентировочное время обработки оставшихся данных.
  • 6. Визуальное отображение % обработанных данных (собственно сам ProgressBar).
  • 7. Скорость обработки данных.

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

Насколько ProgressBar хорош для пользователя, настолько он является головной болью для разработчика. Хорошо когда есть один процесс в одном потоке, когда заранее известно, сколько данных надо обработать и когда длина шага обработки является постоянной величиной. Но когда ситуация не столь проста могут появиться следующие проблемы:

  • 1. Заранее не известно, сколько данных должно быть обработано. Эта проблема очень часто возникает при запросах к базе данных, когда заранее не известно, сколько данных должно быть возвращено базой. Так же не редка ситуация, когда в вычислительных задачах перебор элементов делается по большому набору критериев, в результате чего заранее точно не известно, сколько данных надо перебрать. В обоих случаях оценить общее количество данных все-таки можно выполнив предварительные вычисления или предварительный запрос к базе. Это, конечно, займет лишнее время, но в большинстве случаев это окупается, т.к. информированный пользователь - удовлетворенный пользователь, а неинформированный будет злиться на "глупую программу" которая повисла и не понятно чего там делает. Минус такого решения - оно не подходит для задач, критичных к общему времени выполнения. Второй минус - появляется дополнительный процесс предварительного подсчета, которому так же может потребоваться свой ProgressBar.
  • 2. В процессе выполнения операции невозможно (или можно, но с серьезной дополнительной нагрузкой на систему) узнать процент выполнения операции. Тут все зависит от архитектуры системы и вопрос возвращения данных для показа прогресса надо закладывать еще в момент проектирования. Встраивание этого функционала в уже готовую систему может быть очень сложным и может потребовать "перекраивания" взаимодействия классов и модулей системы. Более-менее стандартные решения: при запросах к базе данных отдавать информацию постранично; в задачах, работающих в основном потоке поднимать события обрабатывающим классом; в задачах, работающих в отдельном потоке периодически опрашивать поток или посылать сообщения из потока.
  • 3. Процесс состоит из набора подпроцессов. Здесь есть, где разгуляться фантазии.
    1. Можно сделать единый ProgressBar и показывать пользователю только сколько времени осталось до конца обработки всех процессов.
    2. Можно к единому ProgressBar-у добавить индикатор наименования выполняющегося процесса.
    3. Можно сделать отдельный ProgressBar для каждого процесса. Это хорошо когда процессов всего 2, иначе в окне будет слишком много информации, что будет путать пользователя.
    4. Можно показывать один ProgressBar, но перезапускать его для каждого процесса. Дополнительно к нему будет полезен лог с законченными и незаконченными процессами.
    5. Можно объединить первый и предыдущий варианты и показывать один общий ProgressBar, и второй перезапускаемый для каждого процесса.
  • 4. Выполняется параллельно несколько процессов. Раньше эта проблема появлялась не часто и, в основном, при закачке данных. Для вычислительных задач распараллеливание процесса вычисления при одноядерном процессоре большого смысла не имела, т.к. одна задача занимала весь ресурс процессора (ну или столько, сколько ей отводилось системой) и запуск второй, параллельной, задачи пропорционально тормозил выполнение первой. Сейчас при появлении CoreDuo и Core2Duo (а в дальнейшем ядер, похоже, будет все больше и больше) можно и нужно пускать параллельные процессы в разных потоках, соответственно вопрос отображения в одном ProgressBar-е нескольких операций выполняющихся в разных потоках становится очень актуальным. Если единицы измерения количественных данных всех параллельных операций одинаковые и операции имеют единую логическую базу – можно показывать единый ProgressBar с суммарными данными. Что делать с операциями, логически не совместимыми, покажет будущий опыт, а пока у меня красивого решения для такой ситуации нет.

Tuesday, October 31, 2006

Новые ExplicitХ проперти в DFM

В Turbo Delphi в DFM стали писаться 4 новыx проперти: ExplicitTop, ExplicitLeft, ExplicitHeight и ExplicitWidth.

Добавляются они сами. В Object Inspector-е их нет и менять их нельзя т.к. они public read only. Объявлены в TСontrol.

Эти новые проперти здорово раздувают файл формы. Но самое противное:

  1. Они постоянно меняются и при добавлении в VSS приходится тратить много времени на их вычитку.
  2. Теряется обратная совместимость формы. Для открытия DFM в ранних версиях надо вычистить все ExplicitX проперти.

Описание этих странных пропертей в хэлпе нет.

Зачем же они нужны?

Внятного ответа от Borland мной не найдено (если кто-то нашел – поделитесь, плиз). В единственном объяснении сказано что в ExplicitX проперти запоминаются значения соответствующих пропертей размеров и расположения, которые были до изменения пропертей Align или Anchor из их дефолтных значений.

Проверяем:

  1. В центр формы кладется панель. Форма сохраняется. В DFM ExplicitX пропертей нет.
  2. Align панели ставится в alBottom. Форма сохраняется. В DFM у панели появились ExplicitX проперти со значениями, равными ее Top, Left, Height и Width, когда она была в центре.
  3. Align панели ставится обратно alNone и… панель сама "прыгает" в середину формы. Форма сохраняется. В DFM ExplicitX пропертей нет.

Мне кажется этот эффект "прыганья" не стоит вышеописанных проблем.

И еще приведенное объяснение явно не полное, т.к. не объясняет эффект появления и исчезания ExplicitX пропертей в TabSheet-ах.


Tags: Turbo Delphi

Monday, October 30, 2006

Генерация XML документации в Turbo Delphi.

Техническая документация на уровне программных объектов, на мой взгляд, может быть нужна в двух случаях:

  1. Сопровождение компонентов, передаваемых сторонним разработчикам. В основном это коммерческие компоненты.
  2. Как внутренняя документация для команды разработчиков.

В первом случае документация фактически является хэлпом - она должна содержать информацию только о public объектах, должна быть соответственно структурирована и оформлена. Информация должна быть подробной – что объект делает, параметры вызова, особенности использования, где расположен (к какому юниту относится), от кого отнаследован. При этом расположение и иерархия являются вторичной информацией и не должны сильно бросаться в глаза.

Во втором варианте документация нужна для быстрого ознакомления с интерфейсами, функционалом и взаимодействием классов. Для более подробной информации есть код (который должен быть хорошо откомментирован). Т.е. подход почти диаметрально противоположный первому варианту. Информация должна быть краткой, и первичным становится расположение объектов.

Меня интересует второй случай. На текущий момент у нас используются обычные комментарии в объявлении классов, переменных, констант и т.д., и для ознакомления с функционалом юнита достаточно просмотреть его interface. Для упрощения взаимодействия между разработчиками и избавления от лишней информации (например вспомогательных переменных) хочется иметь возможность выгружать в отдельный файл объявления классов, всех public и protected членов класса с комментариями к ним и из protected членов класса только те, у которых есть комментарии. Это в идеале. С другой стороны задача, для нас, стоит не очень остро и дополнительных сил на написание своего парсера тратить не имеет смысла. Использование сторонних автоматических документаторов требует изменения формата комментариев и, соответственно, дополнительного контроля за правильностью их написания, что так же не имеет смысла.

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

В Turbo Delphi есть 2 варианта генерации XML документации:

  1. При компиляции приложения создается XML файл для каждго юнита и файла проекта. Для этого должна быть включена опция "Generate XML Documentation" на закладке "Compiler" в "Project Options".
  2. Можно создать единую документацию по всем исходникам приложения. Для этого должен быть включен "Together Support", а сама документация создается из "Model View".

В документацию идет вся информация по объектам + пользовательские комментарии (перед XML комментариями в коде должно быть ///). Вроде бы все хорошо, но…

Документация, создаваемая через "Model View" слишком "тяжелая". Ее, наверно, можно было бы использовать в качестве хэлпа для сопровождения компонентов, но там слишком много внутренней информации, которая в данном случае не нужна. Выбрать же уровень детализации (какие объекты выгружать в документ) нельзя. Насколько корректно работает данный генератор не проверялось т.к. стало ясно, что этот вариант генерации использоваться не будет.

Документация, создаваемая при компиляции на первый взгляд выглядела практически тем что надо. Основная проблема виделась в том, что для класса в XML файл тянется много избыточной информации: все данные по всем предкам. Для пустой формы это более чем 2000 (две тысячи)! строк. Но, в принципе, это не страшно т.к. при парсинге XML избыточную информацию можно игнорировать.

Начинаем экспериментировать с генерацией XML документации при компиляции:

1. Создаем простенький класс с одной функцией. XML комментарии сделаны перед объявлением класса, объявлением функции и в теле функции (последний был добавлен чтоб проверить не добавляет ли генератор комментарии из тела функции к комментариям к самой функции).

...

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

2. Добавляем процедуру с комментарием перед объявлением, тело процедуры без комментариев.

Получаем XML с перепутанными комментариями. Вместо комментария к процедуре вставился комментарий из тела функции

3. Ну если не нравятся генератору комментарии в implementation уберем комментарий из тела функции, но добавим один после всех объявлений.

...

И опять получаем XML с перепутанными комментариями.

Нет ребята, такой документатор использовать нельзя. А жаль, такая идея хорошая...

Saturday, October 28, 2006

Обживаемся в Turbo Delphi

Не смотря на то, что Turbo Delphi, похоже, последняя реинкарнация среды, деваться некуда.

Некуда деваться потому, что нужен нэйтивный код. А последняя реинкарнация (по ощущениям) потому, что рулевые в Борланде не понимают свою аудиторию. Аудитория с ними только из-за нэйтивного кода, а те кому нужны web приложения или .net уже давно от дельфей сбежали и возвращать их бесполезно...

10 лет на Delphi5! Были, конечно, и 6, и 7 и далее по списку, но они в лучшем случае не давали существенных преимуществ, в худшем нещадно глючили :-( Т.ч. отдавать за них денег Борланду категорически не хотелось.

Ну, собственно, о самой Turbo Delphi.

Среда понравилась. Снова хочется писать. Возможно это по контрасту с надоевшей Delphi5? Более яркая раскраска, в принципе, выглядит неплохо.

Борланд решил совсем отказаться от старого варианта среды. Новая похожа на Майкрософтовские IDE. Основные изменения: работа с формой в design time и перенос палеты компонентов в docking window.

Сначала, конечно, пришлось среду "обживать". Раскладка desktop-а идущая по умолчанию (Default Layout) не понравилась т.к. мало места для кода (смотришь как через бойницу дзота).



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



Приятные новшества среды:

  • Более умные шаблоны автодополнения - возможность задавать в шаблоне несколько полей для ввода, переход между полями, автоматическая генерация переменных (например счетчик в for), разворот перечислимых типов (в case). Шаблоны теперь задаются в XML формате.
  • Автоматическая проверка синтаксиса "на лету".
  • Рефакторинг.
  • Автодокументация (правда возможность ее использования под сомнением).
  • Сворачивание отдельных кусков кода (директива {$REGION}).
  • Опция Surround.
  • Возможность закомментировать выделенный фрагмент кода (Ctrl+/).
  • Отдельная раскладка desktop-а во время исполнения (Debug Layout).
  • Поиск компонента в палете по первым символам.

Кое-что из вышеперечисленного появилось еще в BDS2005, но, т.к. BDS2005 была пропущена, считаю их новшествами BDS2006/Turbo. Работается быстрей и приятней.

Минусы:

  • Хотя шаблоны и более умные они, почему то, не вызываются при вводе параметров функции.
  • При установке требует поставить массу всего, что не понятно зачем нужно (зачем компилятору нэйтивного кода на Pascal нужен .netJ# ?).
  • Хэлп ставится при инсталляции по всем продуктам BDS 2006. При контекстном поиске вываливаются громадные списки среди которых приходится отыскивать записи, которые относятся к Delphi.
  • Хэлп написан неаккуратно. После нахождения пары ошибок/неточностей доверие вообще перестал вызывать,
  • Подглючивает Object Inspector. При переключении из Object Inspector в Project Manager и обратно Object Inspector теряет данные текущего контрола и обновляется только после повторного выбора контрола.
  • Debug Layout может быть только один. Т.е. может их быть много, но во время исполнения показывается тот, что с именем "Debug Layout".
  • Не работает User override для Enviroment Variables (была попытка изменить BDSPROJECTSDIR).
  • Попытка работать с Model View вызвала ощущение что его лучше не открывать вообще! Здесь тронешь - там посыпалось. Все изменения в моделе сразу же автоматом меняют код юнита, причем обычно криво.
  • Изменены некоторые ShortCut-ы. Например шаблоны автодополнения вызываются теперь по Ctrl+J. Здесь приведен список основных ShortCut-ов.

После более ближайшего рассмотрения какие то плюсы могут уйти в минусы. А минусы перестанут замечаться или просто выяснится что проблемы из за того что я еще "не умею их готовить".

А вот какие сюрпризы нам преподнесет компилятор будем выяснять в процессе работы.