Если нельзя, но очень хочется, то нужно обязательно и ничего в мире не стоит того, чтобы делать из этого проблему!


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

понедельник, 24 октября 2011 г.

Java for fun: Змейка с помощью TDD

Привет. Не так давно в стенах КПИ был проведен наш мастер-класс по Test Driven Development подходу. Из обратной связи стало понятно, что ребятам хотелось бы увидеть процесс TDD разработки от начала и до конца. По этой причине было снято следующее видео. Исходники можно качнуть тут - Snake.rar).

Итак задание:
Реализовать модель змейки (игра на тетрисе). Поле квадратное, любых размеров, змейка длинной в 2 пикселя. На поле со старта появляется 1 камень и 1 яблоко, при съедании яблока змейка удлиняется и тут же появляется еще одно. Если повернуться на 180 градусов, то она умирает. Если съесть себя, камень или стенку - змейка умирает.

Чтобы попробовать змейку был реализован так же консольный контроллер + UI. Текущая картинка поля выводится на консоль (в виде символов), управление клавишами A+Enter - влево, D+Enter - вправо, W+Enter - вверх, S+Enter - вниз. Просто Enter - продвинуться еще на один шаг.

На этом видео можно посмотреть пример "игры".


А вот и видео многочасовой разработки (устраивайся по-уютнее, бери попкорн и айда смотреть :))


Реализация:
В основном по TDD. В некоторых местах были допущены некоторые часто допускаемые ошибки TDD, а в результате использован дебаг - сделано это для того, чтобы показать к чему ведет нарушение базовых правил:
- После каждой итерации красный-зеленый-рефакторинг должен происходить commit в svn.
- Рефакторинг всегда по чуть-чуть, но отдельной итерацией.
- RnD в коде с помощью дебагера.
- Правило "чуть что сразу rollback". Если чувствуешь, что куда-то не туда зашел - rollback. Если поломано больше одного теста - rollback. Если итерация занимает более полу часа - rollback. После rollback'а последних изменений текущую задачу стоит пересмотреть и упростить.

В принципе, все эти правила можно и нужно нарушать, но это не рекомендуется делать на начальном этапе привыкания к TDD.

Вторая часть видео - 

причесывание проекта при свежем взгляде на него через пару дней. Так стоит поступать с любым кодом, который собираешься сдать. нет предела совершенству, но как минимум 1 раз улучить свой проект стоит. В этом заплыве я не брезгую дебагом, и как и в прошлый раз вы можете проследить сколько процесс отладки занимает времени. Тут же использую самописные моки (хотя мог и воспользоваться, к примеру, EasyMock фреймворком - не делал это сознательно, чтобы было понятна суть объектов-имитаторов), Inversion of Control и Dependency Injection. Тут же была добавлена новая функциональность и проведен достаточно непростой рефакторинг - тесты помогли сделать это быстро и без ошибок.

Если удивляет время, затрачиваемое на написание этого проекта (9,5 + 3 часа) , стоит учесть что:
1) написать как можно быстрее - не было целью этой демки.
2) в ходе записи демки я слушал музыку а все пояснения писал текстом в редакторе, чего не делал бы в реальном проекте. В общем кодил с учетом того, что это будут потом смотреть.
3) в целях образования были допущены ошибки, которые привели к последующей отладке.
4) разработка велась в ночное время (с 10 и до 7 утра), потому как в дневное у нас работа :)
В любом случае, задача была бы решена не меньше, чем за один рабочий день (~6 часов) в лучшем случае.

Если у тебя другие идеи на этот счет и ты программируешь на javа. Присылай свою версию игрушки. Сильно на UI не отвлекайся, хочется видеть движок игры. Целью было написание модели игры, а в UI я написал то, что пришло в голову. Так же советую и тебе поступить.

Как засекать время - на твое усмотрение. Меня же интересует больше другие показатели:
1) сколько потребуется времени, чтобы добавить новую фичу не поломав старый код.
2) посмотреть качество (читабельность) кода.
3) как быстро можно улучшить структуру кода (рефакторинг), по замечаниям от читателей, не поломав игру.

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

Спасибо.

P/S/ Исходный код пригодился в будущем нам в проекте codenjoy.com - радовало то, что я открыл запылившийся код, запустил тесты и продолжил работу как будь-то бы и не отвлекался...

29 комментариев:

  1. Ссылки прикрыли из-за нагрузки

    ОтветитьУдалить
  2. Хехе :) Денег хотят дропбоксовцы. Будем думать куда выложить...

    ОтветитьУдалить
  3. А я вот собрался еще исходнички выложить :)

    И вторую часть, где рефакторил код с моками и добавлял новую функциональность, демонстрируя, как при этом тесты помогают

    ОтветитьУдалить
  4. Выложить без регистрации можно на www.ex.ua

    ОтветитьУдалить
  5. "This email is an automated notification from Dropbox that your Public links have been temporarily suspended for generating excessive traffic. Your Dropbox will continue to function normally with the exception of Public links.

    This suspension is temporary (3 days for the first time)."

    Говорят что три дня не будет света :)

    ОтветитьУдалить
  6. Вот, на EX.UA
    http://www.ex.ua/view_storage/251765475223
    Чуть позже будет еще одна часть

    ОтветитьУдалить
  7. Вот еще на всякий на рапиду скинул

    Демка
    https://rapidshare.com/files/2532412906/Zmeika2.mp4

    Видео разработки
    https://rapidshare.com/files/836631841/Zmeika.mp4

    Исходники
    https://rapidshare.com/files/269191735/Snake.rar

    ОтветитьУдалить
  8. Вторая часть ан рапиде
    https://rapidshare.com/files/198881288/Zmeika3.mp4

    ОтветитьУдалить
  9. а чего так долго? делал в универе тоже змейку, на паскале, без тдд, процедурно и это была первая игра написанная вообще. За вечер успел. Было сделано с помощью консольной графики, с мышкой, менюшкой. И управление норм было, без ентер. А вы за 9 часов, с таким опытом и так слабенько, в чем проблема была?) Это не выпендреж, не упрек, реально интересно. Казалось с таким опытом часа три должно от силы уходить.

    ОтветитьУдалить
  10. Вот оно и интересно, посмотреть на змейку, которая будет готова классическим способом. Вы на java программируете? Если да - жду от вас вашей версии.

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

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

    Как засекать время - на ваше усмотрение.

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

    ОтветитьУдалить
  11. Нет, я не джавист.
    http://floomby.ru/content/A30NOkweFk
    Вот залил свою лабу, без изменений и т.п., как есть (быдлокод тоже есть)). Комментарии вроде есть. Только там под DOS писал, советую через Word открыть, он правильно отобразит.
    Влом было компилить, ибо паскаль уже удалил, а качать лень) Писал под Turbo Pascal 7.0 (если что). Писал где то вечер. Потом уже в течении недели дописывал сохранение и статистику. В итоге где то как вы писал.
    По параметрам. Я вашу змейку не смотрел, но зная принципы TDD, предполагаю что она гибче моей в плане усовершенствованней.
    Хотя я и пытался разбить как можно побольше. В общем тоже интересно Ваше мнение по моей первой игре) Отпишитесь пожалуйста или сюда, или на lagovas@bigmir.net

    ОтветитьУдалить
  12. Спасибо за Вашу версию. Довольно интересно. Предложения, как улучшить код (чтобы его комфортнее было читать):
    - постарайтесь избавиться от всех комментариев на русском, трансформируйте их в имена переменных, названия методов.

    Если какой-то блок кода озаглавлен комментарием - выделите из нее процедуру/функцию и дайте ей название такое, каким был комментарий. Пример, было:
    begin
    cleardevice;
    p.x:=320;
    p.y:=240;
    movemousecursor(p);
    settextstyle(defaultfont,horizdir,2);
    {рисуем кнопки}
    setcolor(white);
    setfillstyle(1,7);
    bar(230,125,430,150);
    bar(230,165,430,190);
    bar(230,205,430,230);
    bar(230,245,430,270);
    {выводим текст}
    moveto(250,90);
    outtext('Complexity');
    moveto(250,130);
    outtext('Low');
    moveto(250,170);
    outtext('Middle');
    moveto(250,210);
    outtext('High');
    moveto(250,250);
    outtext('Back');
    settextstyle(defaultfont,horizdir,1);
    showmouse;
    repeat

    стало
    begin
    cleardevice;
    p.x:=320;
    p.y:=240;
    movemousecursor(p);
    settextstyle(defaultfont,horizdir,2);
    drawButtons; // это новый метод
    printText; // это новый метод
    showmouse;
    repeat

    В случае с переменными - просто дайте переменной имя такое как в комментарии. Пример было

    pos:tpoint; {позиция пазла}

    стало

    puzzlePosition:tpoint;

    Все комментарии трансформируйте в код. Вы пишите программу для человека а не для компьютера.

    - так же все константы, которые невозможно понять без контекста (не глядя на код, в котором они используются) - gd,gm,m,ch,n,i,t1,t2,_s,t,f,s,p - назвать так, чтобы было ясно что они хранят. Это важно по той же причине. Вы пишите код для человека, а не для компилятора (первое сложнее).

    - есть такое понятие как магические константы. Это все числа, которые встречаются в вашем коде непосредственно в месте использования: 230, 125, 430, 150, 165, и так далее. Причем имеет место дублирование - 230 встречается раз 5. Чем это плохо? Просто непонятно почему там 230 а не 1023. Вторая причина - допустим надо исправить 230 на 231. Сколько исправлений потребуется? А что, если где-то забыли? Все константы должны содержаться в константах имя которых отвечает на вопрос, что это за константа.

    Для начала хватит. Позже можно будет рассмотреть лес за деревьями.

    Теперь вопрос, сколько займет времени на исправление этих пока простых замечаний? Как гарантировать, что ничего не поломано? Попробуйте это сделать и сообщите о результате.

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

    Сделаете это, пойдем дальше по code review и поиграем еще с кодом.

    ОтветитьУдалить
  13. Спасибо за потраченное, на мой откровенный быдлокод, время:)
    По поводу комментариев. Писал на русском, что бы преподаватели сразу понимали и другие, у кого с англ. хуже разобрали. Та и самому легче с русскими комментами. По поводу комментарий в название переменной, спасибо, интересное правило.

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

    По поводу тех кратких переменный, gd,gm... Многие из них часто у всех использовались, потому для всех они были понятны. Особенность написания гуи на паскале. Gd - graphic driver, gm - graphic mode. t1,t2 тоже вроде, time, хотя лучше конечно же time, но опять же, писалось все это в вечер перед лабораторной :)

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

    По поводу исправления замечаний. В общем то разбить на функции, добавить буквы переменным не займет много времени. Минут 20. Хотя еще время уйдет на разбирательство с прогой, что бы нормально разбить по функциям. Хотя честно говоря, мне в этот код даже лезть не хочется) Во первых паскаль, во вторых это "быдлокод")

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

    Вас действительно так задел мой комментарий и хотите еще посравнивать два подхода?)

    ОтветитьУдалить
  14. Я писал ответ на Ваш комментарий. Вы его удалили или оно плохо отправилось? Спрашиваю что бы знать, стоит еще раз писать или нет.

    ОтветитьУдалить
  15. Правда? Я не удалял :) Вот ваш текст на всякий...

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

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

    По поводу тех кратких переменный, gd,gm... Многие из них часто у всех использовались, потому для всех они были понятны. Особенность написания гуи на паскале. Gd - graphic driver, gm - graphic mode. t1,t2 тоже вроде, time, хотя лучше конечно же time, но опять же, писалось все это в вечер перед лабораторной :)

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

    По поводу исправления замечаний. В общем то разбить на функции, добавить буквы переменным не займет много времени. Минут 20. Хотя еще время уйдет на разбирательство с прогой, что бы нормально разбить по функциям. Хотя честно говоря, мне в этот код даже лезть не хочется) Во первых паскаль, во вторых это "быдлокод")

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

    Вас действительно так задел мой комментарий и хотите еще посравнивать два подхода?) "

    ОтветитьУдалить
  16. Я когда отправлял, оно отобразилось, но при обновлении пропало, браузер Опера. А второй раз было бы лень заново писать, потому спросил на всякий случай :)

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

    На счет "быдлокода". Есть такая пословица: "сегодня суперкод - завтра быдлокод". не стоит быть таким категоричным. Тот код что я написал на второй день мне казался быдлокодом, и даже после исправлений сегодня я найду еще ряд причин почему он плох, но даже если я исправлю все все замечания, то завтра прочитав новую статью или книгу я посчитаю его говнокодом. Так будет всегда. Более того, какой бы мой код не был идеальный - другой человек взглянул в на него скорее всего посчитает код говнокодом и предложит ряд предложений. Так что все норм. Читайте книги, фиксайте свой код, показывайте его людям, делайте выводы.

    "...правка не должна быть сложной" - ну, это наверное так, но все же хотелось бы на практике проверить так ли это?

    "...так задел мой комментарий и хотите еще посравнивать два подхода..."

    Любопытно понять и сравнить два подхода. Я всегда хотел это сделать, но потом в какой-то момент отказался от этого - понял, что так как раньше кодить уже точно не буду. Но все же есть много людей, которые не верят в ТДД - говорят "как жеж, писать в три раза больше кода? это расточительство!" Странность в том, что говорят так те, кто не пробовал или пробовал, да не получилось. Все кто проникся идеей и опробовал этот подход на продолжительном периоде - все как один говорят, я как раньше уже не буду, потому что это гон!

    И все же. Если быстрее по классике - быть может ну его этот ТДД и попробовать что-то другое. Для этого и предлагаю попробовать.

    ОтветитьУдалить
  18. Я если что, считаю TDD удобной и полезной вещью, я не противник этого подхода) Сам не использовал, пока не приходилось, но читал пример и мне он понравился. Конкретно понравилось то, что знаешь четко что требуется, и не пишешь в последствии лишнего кода. Избавляет так сказать от "выпендрежа". Вот щас тут такую фичу добавлю, там такую, еще не написав полную прогу, в итоге иногда может получиться каша. А здесь четко видишь когда прога работает как надо. Но это лично мое мнение. Плюс вроде не так уж и долго или много его писать. По крайней мере мне так кажется. Это ведь просто подсунуть левые данные, которые ты знаешь, а не писать обработку всех данных вообще. Плюс стимулирует писать норм классы ексепшенов. По крайней мере примерчики TDD которые я смотрел были показаны за счет тестирования ексепшенов (python).

    ОтветитьУдалить
  19. Кстати, не совсем представляю с кем говорю. Вы тот, кто вначале рассказывал про TDD, или тот, кто кодил вначале?) Подозреваю, что первый вариант.

    ОтветитьУдалить
  20. И, все же - как на счет провести эксперимент со змейкой? :) Будет желание вспомнить паскаль - буду рад. Я тоже в прошлом на паскале и делфях писал.

    Я, вариант тот, который с микрофоном бегал туда-сюда :)

    ОтветитьУдалить
  21. я не против, главное найти время и желание) А то такая лень лезть в этот свой код, зная что писал на спешку и помня что она не очень для модификаций больших. Как сделаю, отпишу здесь время за сколько сделал.
    Кстати, как Ваше впечатление от КПИ, мастер-класса у нас, от публики и вопросов (вопросы не очень вроде были)?

    ОтветитьУдалить
  22. Ок, договорились :) Лезть в код, который старый, а быть может написан кем-то другим вам прийдется частенько, если вы собрались в айти разработчиком устраиваться. Потому посоветовал бы прививать привычку лезть по локоть в грязь уже сейчас. От этого можно даже получать удовольствие.

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

    Аудитория замечательная. Мне очень понравилось.

    Кстати сегодня буду помогать Харьковским коллегам на очередном мастер-классе.

    ОтветитьУдалить
  23. По поводу ваших тренингов. Было бы прикольнее, что бы вы брали кого то из аудитории, и что бы параллельно кодили. Пускай разные языки и т.п., но одно задание, и поочередно комментировали. Один покомментил, второго спросил, хочешь чет сказать, если да, он говорит. А еще лучше, если кто то это сможет делать с ТДД. Тогда в двойне тестирование. Парное программирование против непарного и ТДД против не ТДД. Мысль пришла на следующий день. Я бы с удовольствием вышел бы, попробовал бы писать на питоне и с ТДД. Как по мне, было бы интереснее) Другое дело что не каждый может решиться)

    ОтветитьУдалить
  24. Интересная идея! Спасибо, обсудим с напарником

    ОтветитьУдалить
  25. Ну тогда если не секрет, расскажите потом, что решили, что бы потешить себя, прошла моя идея или нет)

    ОтветитьУдалить
  26. Более того, поделюсь отзывами студентов на этот счет, если решимся с напарником на это.

    ОтветитьУдалить
  27. Парень был в аудитории. Пока мы рассказывали (около 2-3 часа) - он реализовал змейку. Вот, ждем его кода, чтобы посмотреть на результат. А в целом идея пошла на ура. Спасибо.

    ОтветитьУдалить
  28. И каковы результаты? Он писал с ТДД или без? Хотя результаты интереснее было бы сразу в аудитории определять) Всем же любопытно.

    ОтветитьУдалить
  29. Он писал без TDD. Он справился быстрее раза в три. Змейку не тестили. Код так и не видел. Из этого всего выводы пока рано строить. Но в ходе обсуждения вопроса "и в чем же ускорение" мы пришли к тому, что ускорение стоит рассматривать в связке качество+время.

    Хорошенько вспомните тот период из жизни своего проекта, когда спустя некоторое время очень не хотелось в него лезть, потому что, что бы ты не сделал - проект ломается. Проект хрупкий. Лучше не трогать. А если надо - лучше переписать. Вот в этом состоянии умирает любой проект на который не писали unit тесты (не важно по tdd или без). Проект мертв, когда с исправлением одной ошибки вносятся две. Дальше развитие не происходит. В проекте с хорошим покрытием юнит тестами разработка будет продолжаться еще долго после этой точки.

    Вот к чему мы пришли в ходе сравнения.

    ОтветитьУдалить