Когда встроенного рефакторинга нема…

…приходится вот так развлекаться

Зачем? А затем что было много DAO/Repository с примерно вот таким кодом:

т.е. для случаев когда в domain model есть интерфейс и его реализация, хотя были и такие случаи при которых только один класс без интерфейса:

а после некоторого рефакторинга хотелось бы получить код поаккуратней, вот такой для первого случая:

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

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

А в свежевыдшедшей версии Intellij IDEA она тебе не только подсветит подпадающий под regex текст, но и покажет на что его заменит

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

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

И вот оказывается в IDEA (платной версии) и ReSharper - есть такая штука как Structural Search / Search With Pattern, которые позволяют использовать PSI-дерево, которое построила среда разработки, вот простенький пример, в данном случае для того чтобы найти все перечисления для которых выполняется некоторая проверка:

Подробнее можно прочитать здесь:

http://www.slideshare.net/intelliyole/implementing-refactorings-in-intellij-idea-presentation http://www.jetbrains.com/idea/documentation/ssr.html http://blogs.jetbrains.com/dotnet/2010/04/introducing-resharper-50-structural-search-and-replace/ http://www.jetbrains.com/resharper/webhelp/Reference__Search_with_Pattern.html

У такого подхода еще немало преимуществ просто в плане удобства - не надо предусматривать все варианты форматирования - посколько для шаблона по которому мы ищем то же строится семантическое дерево дерево (если отметить Match similar constructs) - и ищется именно семантическое соответствие!

8 notes

Подпишитесь на новые статьи в VansickleHQ /russian edition/ по Email или RSS

#regex#refactoring#search

Поля-списки в объектных БД

Интересный момент с сохранением свойств-списков в объектных БД.

Если мы рассмотрим ORM как образец - то как мы привыкли там - в ActiveRecord или в Hibernate, мы должны неким образом (через схему или маппинг) описать какие свойства сохранять, то их ORM будет сохранять - в том числе и списки. Т.е. хотя описываем мы списки неким специальным образом ( или или has_many каким), но по сути просто как еще одно свойство:

И с этого момента ORM начинает управлять коллекцией - если я что в нее добавлю, удалю или еще что-то, то изменения сохранятся в БД (в случае hibernate конечно есть еще вариант с invert, но разговор не о том).

А с объектными БД, в моем случае - Db4o, описания по умолчанию не требуется - ты просто сохраняешь объект в БД и потом получаешь его обратно (код в упрощенно-сыром виде, для наглядности):

Т.е. выше был весь код чтобы сохранить объект - ничего более не требуется. Но вот на днях обнаружил проблему что если к выбранному в примере объекту-статье попробуем добавить комментарии - то все станет работать не совсем так как представлялось по опыту с ORM. Для моей проблему это выглядело так что первый “комментарий” к статье сохранялся - но всегда оставался один, больше одного почему-то не сохранялось.

Т.е. вот этот тест заканчивается ошибкой - вместо двух комментариев у статьи сохраняется только один:

Оказывается что причина в том что Db4o рассматривает списки, так же как и остальные ссылочные объекты. Т.е. если у меня есть объект, в нем есть какие-либо поля, то механизм БД сохраняет изменения этих полей, но если мы там держим ссылку на некоторый объект то механизм сохранит ссылку и объект на который указывает данная ссылка, только в случае изменения. А если изменения были внутри объекта на который указывает ссылку то механизм естетсвенно не сохраняет их, поскольку если бы он всегда анализировал и сохранял изменения для всех полей, то граф объектов для которых надо сохранить изменения мог получаться очень большим. Собственно так же работают и ORM - если нужно чтобы сохранились изменения и в объекте на который мы сохраняем ссылку то требуется это указать с помощью cascade, но это не касается коллекций. А поскольку Db4o рассматривает список просто как еще один объект на который есть ссылка, а когда я добавляют новый элемент - то я изменяю именно список а не тот объект который сохраняю - то и измнения в список не сохраняются.

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

Ну на мой вкус, логичнее было бы чтобы механизм БД был бы aware of списков (т.е. по умолчанию обновления в списках сохранял, а не рассматривал их как еще один ссылочный тип), поскольку как еще с ними работать кроме как с поддержкой обновлений. Надо будет на досуге посмотреть как ведут себя другие объектные бд (neodatis или ravendb например) при сохранении полей-списков.

4 notes

Подпишитесь на новые статьи в VansickleHQ /russian edition/ по Email или RSS

#db4o#objectdb#lists#collections#.net#java#orm

Еще немного по датам

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

И вот как-то тихо перестали работать тесты в которых он задействован. С замечательным java.text.ParseException: Unparseable date: “Fri Jan 14 02:12:21 +0300 2011” - который нисколько не подсказывает в чем дело.

Перепроверил еще раз, даже нашел ошибку в формате - было hh вместо HH - а hh ведь для 1-12 (т.е. 12-часового, am/pm cтиля), а мне надо было 0-23. Но проблема была не в этом. Заметил что те же тесты нормально работает в некотором ином окружении, и выяснилось в чем дело - проблема в локали, вот эти Fri, Jan - они локализуются и правильный код вот такой:

Подпишитесь на новые статьи в VansickleHQ /russian edition/ по Email или RSS

Пример плохого интерфейса

И более чем распространненого - PayPal, заходим в свой профиль. В нем есть список меню, с “выпадающими” элементами.

Когда применяется такое меню то абсолютно стандартное восприятие этого - это то что “выпадающее” меню и есть все содержание верхнего элемента. Т.е. в указанном выше меню Profile есть только 4 пункта - редактирование Email, адреса, кредитки и банковского счета. И вот пробегаешь по этим меню и возникает вопрос - блин, а где же периодические платежи то найти?

А причина в том что разработчики вывели в выпадающий список не все содержание, а только его часть - и при нажатии на Profile мы получаем все доступные функции, в том числе периодические платежи которые мне надо было посмотреть:

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

7 notes

Подпишитесь на новые статьи в VansickleHQ /russian edition/ по Email или RSS

#gui#usability#patterns

Easy mechanics of everyday things

Как Punto Switcher (а также Keyboard Ninja, Key Switcher n etc.) “на лету” меняет раскладку для уже введеного слова в любом редакторе?

Перехватывает введенные символы, и анализирует, как только под шаблон подходит - просто выдает редактору количество backspace равное количеству введенных символов, а потом символы уже “переведенного” слова. Аналогично реализуется обычно простенькая анимация в коммандной строке - например если это консольный загрузчик данных какой-то - то он обновляет информацию сколько скачано / сколько осталось похожим образом.

Как реализуется докачка файл из веба?

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

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

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

RSS2LJ[vansickle]c18d115464bb8ece69acbfc0d8718f75

Подпишитесь на новые статьи в VansickleHQ /russian edition/ по Email или RSS

Time Tracker

Когда-то поставил плагин Time Tracker для Chrome (основного браузера на портативных машинах), надо признать статистику он собирает кажется как-то странно - как например dealextreme попал на третье место в список непонятно абсолютно, равно как и еще парочка. Однако при всем при этом главные места в списке держит рабочий трекер, Красота :)

1 note

Подпишитесь на новые статьи в VansickleHQ /russian edition/ по Email или RSS

#timemanagement

Пример маленькой своей ниши

Вот наткнулся http://sqljet.com/ - pure-java версия SQLite, не единственная конечно и под .net/mono есть реализации.

Интересно, что авторы sqljet так же делали http://svnkit.com/ - т.е. это подход такой - берется известный проект-библиотека которым многие пользуются (svn, sqlite) и имплементируется managed-версия, полностью поддерживающая все возможности - т.е. пользователь этой библиотеки может работать с БД sqlite или с репозиторием svn не собирая официальные бинарники для каждой платформы и обращаясь к ним с помощью какой-либо interop-технологии (вот например - http://sqlite.phxsoftware.com/ - этой я пользовался в свое время, для каждой платформы своя mixed-dll, или даже несколько), а используя только одну библиотеку под JVM или CLR.

Соответственно делается проект dual-license, для open-source под GPL, а если вам надо в коммерческом ПО подобную библиотеку - то извольте лицензию покупать, и тот же svnkit много где использовался (в eclipse, intellij idea например) и судя по пользователям на сайте весьма успешен.

Казалось бы совсем не очевидно что на это можно зарабатывать - но выходит хороший пример вполне нормальная маленькая ниша в которой можно работать и зарабатывать.

Подпишитесь на новые статьи в VansickleHQ /russian edition/ по Email или RSS

Почему платность - это бывает хорошо

Как писал - Рефлектор стал платным http://ru.vansicklehq.com/post/3070029552/reflector (хотя там теперь какие-то неловкие обещания на тему доступность бесплатной версии появились). Но что интересно - как это повлияло на сообщество.

Довольно быстро появилось много альтернатив, до этого их не было вообще, всех устраивал рефлектор и все им пользовались.

А теперь у нас есть куча вариантов:

Интересно кстати что когда точно так же NCover перешел из бесплатного в платный вид такой реакции не было, PartCover никто особенного допиливать не стал, а dotCover появился много позже.

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

Недавно Гугл объявил что Google Maps только в некотором ограниченном объеме будут бесплатны для сторонних разработчиков, и хотя как разработчика-потребителя такого сервиса это не радует, но с другой стороны наверняка это вызовет всплеск развития OSM, Bing Maps, Yahoo Maps, Yandex Maps.

Подпишитесь на новые статьи в VansickleHQ /russian edition/ по Email или RSS

#.net#decompile

Dual-mount SD card

Первая удобная системная вещь в Android, которой не было в WM - разделяемый доступ к карте памяти.

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

https://market.android.com/details?id=com.rafoid.multimountsdcard.widget https://market.android.com/details?id=com.protocol.x.USB

Эти программы монтируют карточку таким образом, что она доступна одновременно и девайсу и ББ. Монтировать можно либо с помощью удобного виджета в один клик, либо автоматически.

Естественно для подобного требуется root-доступ к ОС телефона и не забыть сделать бекап.

Подпишитесь на новые статьи в VansickleHQ /russian edition/ по Email или RSS

#android

You must kill it.. every time

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

Т.е. вот такой код - источник утечек памяти

           LoginForm form = new LoginForm();
           form.ShowDialog(this);
           String login = form.Login;

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

Соответственно чтобы сделать код правильным, мы должны добавить Dispose:

           LoginForm form = new LoginForm();
           form.ShowDialog(this);
           String login = form.Login;
           form.Dispose();

Но лучше использовать такую конструкцию (т.е. как и с любым системным ресурсом, файлами например):

           using(LoginForm form = new LoginForm())
           {
               form.ShowDialog(this);
               String login = form.Login; //собственно, я думаю что ничего страшного не будет если эта строка будет и за пределами using, но лучше все же внутри
           }

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

Отсюда вывод - создали любой элемент GUI - думайте где и как для него будет вызываться Dispose.

1 note

Подпишитесь на новые статьи в VansickleHQ /russian edition/ по Email или RSS

#.net#gui#winforms