понедельник, 6 декабря 2010 г.

Java Generics vs Наследование

Вообщем по просьбе Viktor Davion объясняю (в 140 твиттеровских символов не уложился), отчасти, что побудило к посту ненависти. Под катом, т.к. содержит плоды работы воспалённого мозга.

пятница, 5 ноября 2010 г.

Code coverage для интеграционного тестирования

Итак, ситуация следующая. Есть у нас Integration Test Framework, написанный специально для нашего приложения. Но начался он писаться задолго после старта работы над приложением, поэтому накопилось большое количество кода, который тестируется в лучшем случае unit тестами. Как это всё вместе работает не совсем понятно :) Для это писался этот ITF, но он ещё в разработке и тесты пишутся достаточно медленно. Вот в процессе обсуждение этого факта, мне подумалось, а почему бы не считать каково покрытие тестами у нас. Вообще, было замечено, что многие программисты очень плохо относятся ко всяким таким характеристикам как сode coverage. Вот для unit тестов оно у нас не считается, но локально иногда запускаешь посмотреть, сейчас у нас больше 80%. Но, как мне сказали, о чём это говорит? Из за неправильного ответа на этот вопрос и возникает такая не любовь к этой величине. То, что сode coverage 100% это не значит, что вы достигли просветление, ваше приложение идеально и вы на пути к нирване. Но это говорит о том, что весь написанный код, действительно вызывается, т.е. это немного спасает от простых ошибок.

Но вернёмся к ITF. Почему я считаю, что для интеграционных тестов сode coverage более полезен.

  1. Меньше возможностей для искусственной манипуляции результатами. Как иногда бывает с простыми численными характеристиками, за что их и не любят, люди увлекаются попыткой их достижения. И если мы говорим о unit тестах, то достаточно просто написать много, по своей сути бесполезных тестов, которые дадут требуемую цифру. Это ещё одна из причин, почему test first :) Но для интеграционных тестов это не так. Они не работают напрямую с методами или классами. Они используют исключительно пользовательское api, создание файла, нажатие на кнопку и т.п. Поэтому пытаться искусственно делать 100% покрытие очень трудоёмкая задача. Кроме того, даже для ручного тестирования, многие все равно прикручивают сode coverage, чтобы увидеть это заветное число, после того как тестировщики погоняют приложение. Это даёт много интересной информации для размышления.
  2. Мы оказались в ситуации, что интеграционные тесты приходится писать вдогонку. Уже есть большая часть приложения, команда разрослась почти до 10 человек и всего 2 тестировщика. Таким образом, отслеживая величину покрытия, можно делать выводы о том, мы догоняем или всё таки уже опоздали. Т.е. если на предыдущей недели было 20%, а на этой стало 23%, то мы на верном пути, но если стало 10%, то слишком увлеклись генерированием не проверенного кода и наверно стоит поднапрячь программистов, чтобы они побольше внимания уделяли написанию интеграционных тестов в помощь тестировщикам.

В результате всё таки создал такой таск и решил на досуге посмотреть, что может предложить open source java community поэтому поводу. Надо была попроще библиотека, которая позволяла посчитать покрытие кода, по результатам работы приложения. начал конечно же с Open Source Software in Java. В принципе, предложения есть. Я выбрал последнее с "оригинальным" названием CodeCover. Последняя версия можно считать 1.0.* оформленная ажно в 2009 году, но плагин под эклипс обновлялся в 2010, да и остальные тулы, тоже не сильно новее. Маленький проектик hoto можно увидеть под катом, если заинтересовало.

суббота, 30 октября 2010 г.

Субботний вечер пропаганды маргинальных технологий: hgsubversion

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

Сегодня хотел немного затронуть тему распределенных систем контроля версий. На самом деле, эта тема достаточно популярно, просто больших компаниях приживается очень плохо, некоторые, как я узнал, например в IBA, вообще cvs пользуются. Подробно рассказывать именно про DVCS не думаю, что есть смысл, материалов на всех языках мира хватает, как в общем про DVCS, так и про конкретные системы. Один из главных зачинатилей всего безобразия, это Линус, так что в качестве введения можно посмотреть его выступление в кампусе Google, где в его духе эмоционально рассказано кто не прав и почему :)

Я расскажу про немного другую проблему. Когда вы делаете не свой продукт, а под заказ или являетесь всего лишь небольшим подпроектом огромного проекта, часть технологий вы выбрать не можете и вынужденны использовать subversion. Но ведь хочется вкусненького, и это возможно. Почти все разработчики DVCS понимают, что все сейчас прям не бросятся переводить свои репозитории на новые рельсы. Поэтому присутствует интеграция с многими другими системами контроля версий. Например с subversion. Вот хотел бы рассказать про hgsubversion, проект, который позволяет интегрировать subversion и mercurial репозитории. Был опробован на реальном проекте и пока никаких проблем не возникло.

пятница, 29 октября 2010 г.

Требую больше immutability!

Вообщем тривиальная слоган, его можно прочитать даже в достаточно старой Joshua Bloch: Effective Java, Item 15: Minimize mutability.

Под катом чутка банальности, так как сегодня доделал и наболело.

пятница, 22 октября 2010 г.

Java сompile time annotations

Ох.. тут давече затеяли большой рефакторинг проекта в рамках которого я решил сделать пару классов immutable. Казалось бы, делаем все поля private + final и всё ок. Но нет, для ссылочных типов это не работает. Так как в этом случае мы не можем изменить ссылку на этот объект, которая храниться в поле, но за то можем изменить сам этот объект, например добавить в коллекцию ещё пару объектов. Тут есть два подхода, оба приводят к равно ценном результату (immutable классу):

  1. Требовать, чтобы все ссылочные типы также были immutable, как String например. Для коллекций можно использовать Collections.unmodifiableCollection метод.
  2. Второй способ заключается в основе ООП: инкапсуляции. Ни один метод не возвращает объект по ссылке, только его копию, например вместо возврата коллекции можно вернуть её неизменяемую копию с помощью описанного выше метода.
Вообщем то всё достаточно очевидно. Ввиду ряда особенностей и уменьшения последствий рефаторинга, посоветовавшись, я решил воспользоваться вторым способом. Но пока рассматривал первый метод, захотелось аннотацию, которая бы сигнализировала, что объект не изменяем и корректность класса проверялась на этапе компиляции. Если кого-то заинтересовала, краткое описание под катом.

четверг, 16 сентября 2010 г.

Log4j file header

Давно не писал, но тут накипело. Читал буквально позавчера статью Модульный дизайн, или «что такое DIP, SRP, IoC, DI и т.п.». Вот там говориться про великий Log4j, какой он распрекрасный. И вот понадобилось мне сделать header для файлов. Такой вот конфиг:
  1. <appender name="eventhistoryfile" class=" org.apache.log4j.RollingFileAppender">  
  2.     <param name="LogFileName" value="${log4j.file.name}"/>  
  3.   
  4.     <layout class="org.apache.log4j.PatternLayout">  
  5.         <param name="ConversionPattern" value="${log4j.pattern}"/>  
  6.     </layout>  
  7. </appender>  
Очень простой.. Как добавить хедер? Оказывается PatternLayout содержит пустую реализацию метода getHeader. Ну и как это называется??? Ладно, сделал свой, который добавляет эту функциональность, не сложно. Идём дальше. Запустил, работает. Вырубил приложение, запустил ещё раз появился ещё один header в файле О_о я нахожусь в шоке. Пришлось ещё наследоваться от RollingFileAppender и переопределять у него метод writeHeader, чтобы header в файл писался один раз (код взят в исходном FileAppender  только добавлена последняя проверка).
  1. @Override  
  2. protected void writeHeader() {  
  3.     if (this.layout == null) {  
  4.         return;  
  5.     }  
  6.     String header = layout.getHeader();  
  7.     if (header == null && this.qw == null) {  
  8.         return;  
  9.     }  
  10.     File f = new File(this.getFile());  
  11.     if (!f.exists() || (f.exists() && f.length() == 0)) {  
  12.         this.qw.write(header);  
  13.     }  
  14. }  
Вообщем, негодую.

вторник, 22 июня 2010 г.

Crash Racing

Для получения Диплома осталось только подписать обходной лист. Вот разбирал архивы, чтобы всё накопленное записать на dvd и сдать в архив, и наткнулся на прикольный проект. На втором курсе у нас Java вёл очень хороший преподаватель Гафуров Сергей Владимирович. И в конце курса  мы разбились на команды и где-то недели две писали сетевую игру на Java. Мы выбрали гоночки и как видно из заголовка поста - разрушительные гоночки. Это был невероятный проект и очень полезный, заставил по другому взглянуть на программирование. Всё таки работа в команде, это работа в команде :) Где-то в середине проекта стало понятно, что мы не успеваем и пришлось перебороть юношеский максимализм и волевым усилием отказаться от оружия. Зато к сроку у нас был работающий проект, хоть и разрушать ничего нельзя было. Вот на память записал видео сражения 4х ботов на моей любимой карте, за ними наблюдать иногда веселее чем самому играть, а благодаря Диме Гончарову выиграть у них практически не возможно =DD это удавалось только Диме Хоревичу. Кстати да, о лицах :) Первый Дима как не сложно догадаться работал над физикой и ИИ, второй над UI, Катя Цвирко сделала потрясающие карты и работала с панелью игрового поля. А я скромно писал сетевое взаимодействие. Играть можно было по сети. Так как ботов было трудно победить - сражались между собой :) Игра не поражает воображение, но это всё таки учебный проект второкурсников, причём в жатые сроки, мне нравиться ^__^



Код не покажу, стыдно :)

Upd: Видео уже лень делать, вот скриншоты :)