вторник, 28 декабря 2010 г.

Kanban vs Scrum

Привет всем. 

Сегодня тема не совсем соответствует названию сайта, но я думаю что с автором мы договоримся. 

Последнее время набирает популярности новая техника управления проектами под названием "Канбан", которую как обычно придумали японцы. Вот уж до чего непоседливые. Сегодня я попытаюсь немного рассказать о канбане.

Для начала посмотрим, что нам говорит Вики по поводу канбана

Канбан (камбан) — система организации производства и снабжения, позволяющая реализовать принцип «точно в срок».

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

Вот ссылочка на книгу: Scrum и Kanban: выжимаем максимум от Хенрика Книберга и Маттиаса Скарина. Книга на русском и помогает сделать первый шаг в понимании того, что такое канбан. 

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

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

Например у нас есть команда: 5 программистов. Ставим ограничение на количество задач в 9. Дальше у нас есть проект с набором задач, количество которых конечно же огромно. Лид (продюсер, владелец продукта) выбирает первые 5 задач и программисты начинают работать над ними. И вот по каким-то причинам они не могут закрыть ни одну из задач, тогда программист, который заблокирован приостанавливает свою задачу и берет новую у лида. Таким образом в работе оказывается уже 6 задач. Если у команды накапливается 9 незакрытых задач, то она больше не может брать новые задачи, а должна во что бы то не стало закончить хотя бы одну из уже взятых задач. Такой подход позволяет увидеть проблемы проекта очень быстро, поскольку мы обязаны завершать все задачи, чтобы получать новые и если где-то образуется затык, то вынуждены его исправлять, а не откладывать на будущее. 

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

Когда же такой подход наиболее приемлем? На каком проекте или на какой стадии проекта?

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

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

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

Единственное, что упустил, это слабые стороны и недостатки канбана. Оставлю это вам и буду рад услышать ваше мнение. 

Всем доброй ночи.

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

Command Window and Macros in Visual Studio. Part 1

Последнее время приходиться писать на Lua, а также постоянно работать с командной строкой.
Лучшим вариантом для редактирования Lua является блокнот Notepad++. Во всяком случае я пока ничего лучше не нашел. Бесплатная лицензия, наличие широкого выбора плагинов, хорошая скорость работы делают его очень привлекательным вариантом. 
Но не хочется переключатся в блокнот, студия ближе, да по ходу работу иногда приходиться править С++ код, искать различную информацию в С++ коде. А в данном моменте студия пока обходит блокнот.

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

Вариант первый - плагин.
Это самый первый вариант, который приходит в голову, особенно когда спишь и видишь славу Visual Assist-а и ReSharper-а. Сразу тянет создать свой аналогичный плагин только под Lua. 
Создал и сел думать, а какой же функционал туда забить. Не придумал, вернее его было так много, что на десяток плагинов хватит. А вот первой ключевой фичи, которая которая потянет за собой остальные я не нашел. Потом я нашел еще несколько причин не использовать плагин:

  • неясен функционал
  •  

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

  • необходимость думать о визуальном интерфейсе
  •  

И тогда я наконец понял, что мне нужно. 

Вариант второй - СКРИПТ.
Но не просто скрипт, а скрипт, который может взаимодействовать со студией, править файлы проекта, получать информацию о классах и т.д.
В качестве отправной точки поисков я выбрал CommandWindow. Здесь меня ждал сюрприз. 
CommandWindow позволяет делать все, вызвать любую команду, которая есть в студии. 

При наборе команды появляется окно подсказка с вариантами команд

Например команда File.OpenFile
Синтаксис следующий "File.OpenFile filename". Причем при наборе имени файла студия тут предлагает варианты, не хуже чем это делают Visual Assist и ReSharper.

Также есть еще одна дополнительная возможность: присвоение команде короткого имени. Это избавляет от необходимости писать длинную команду. Вот вариант короткой команды File.OpenFile

Команда для создания синонима команды называется Tools.Alias или просто alias. Если просто вызвать alias, то можно посмотреть список уже заданных сокращений
Кроме того она позволяет создать синоним для команды с определенным набором параметров. Например после вызова "alias openSealedFile File.OpenFile SealedClass.h" я смогу использовать openSealedFile для открытия файла SealedClass.h

И на закуску еще один бонус - CommandWindow работает в Visual Studio Express Edition

Все это хорошо, но не хватает еще одной вещи - собственно самих скриптов.
И об этом я расскажу завтра
А на сегодня еще один вопрос в качестве домашнего задания: какие еще интересные возможности Visual Studio вы знаете и используете?

Всем доброй ночи.

четверг, 28 октября 2010 г.

Выделение памяти в векторе

Всем привет
Решил сделать простенькую программу, которая бы наглядно показывала разницу между метода resize и reserve в классе vector из стандатной библиотеки.
Программа реализовалась за 10 минут:

#include "stdio.h"
#include "iostream"
#include "vector"
using namespace std;

int main()
{
std::vector intVector;
printf("Start: Size = %i, capacity = %i\n", intVector.size(), intVector.capacity());
intVector.resize(10);
printf("Resize 10: Size = %i, capacity = %i\n", intVector.size(), intVector.capacity());
intVector.reserve(25);
printf("Reseerve 25: Size = %i, capacity = %i\n", intVector.size(), intVector.capacity());

for(int i=0;i<16;i++)
{
intVector.push_back(i);
}
printf("Push_back: Size = %i, capacity = %i\n", intVector.size(), intVector.capacity());

return 0;
}

Все отлично работает. Красота.
А потом вспомнил, во всех книгах говорят о том, что размер вектора при добавлении новых элементов увеличивается в зависимости от некого коэффициента.
И решил, а давай ка проверю.
Проверяется очень просто переопределяем операторы new/delete и смотрим, сколько памяти реально выделяется при изменении размера вектора. Ниже код:

char mem[4096];
int startFreeBlock=0;

void* operator new(size_t blockSize);
void* operator new[](size_t blockSize);
void operator delete(void* block);
void operator delete[](void* block);

void* operator new(size_t blockSize)
{
int oldstartFreeBlock = startFreeBlock;
startFreeBlock += blockSize;
printf("New = %i, Adress = %i\n", blockSize, (int)(&mem[oldstartFreeBlock]));
return &mem[oldstartFreeBlock];
}
void* operator new[](size_t blockSize)
{
int oldstartFreeBlock = startFreeBlock;
startFreeBlock += blockSize;
printf("New = %i, Adress = %i\n", blockSize, (int)(&mem[oldstartFreeBlock]));
return &mem[oldstartFreeBlock];
}
void operator delete(void* block)
{
printf("Delete = %i\n", (int)(block));
}
void operator delete[](void* block)
{
printf("Delete[] = %i\n", (int)(block));
}

Код собрал и запустил под двумя компиляторами и вот результаты:

gcc-4.4.1:
Start: Size = 0, capacity = 0
New = 40, Adress = 4673568
Resize 10: Size = 10, capacity = 10
New = 100, Adress = 4673608
Delete = 4673568
Reserve 25: Size = 10, capacity = 25
New = 200, Adress = 4673708
Delete = 4673608
Push_back: Size = 26, capacity = 50
Delete = 4673708

Visual C++ 2008 Express:
Start: Size = 0, capacity = 0
New = 40, Adress = 4325880
Resize 10: Size = 10, capacity = 10
New = 100, Adress = 4325920
Delete = 4325880
Reseerve 25: Size = 10, capacity = 25
New = 148, Adress = 4326020
Delete = 4325920
Push_back: Size = 26, capacity = 37
Delete = 4326020

Как и ожидалось, в случае явной просьбы изменить размер вектора с помощью методов resize и reserve, приложение послушно все делает. Вектор увеличивается ровно до того размера, который я запросил. А вот в случае неявного изменения размера вектора при вызове метода push_back компиляторы делают это по разному. Компилятор gcc выделяет в два раза больше памяти чем Visual C++.

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

Всем доброй ночи.

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

Живи действуя

Привет всем.

Несколько раз за неделю наталкиваюсь на мысль о том, что достижение нирваны без действия невозможно.
В первую очередь это был перевод статьи "6 Tips To Stop Talking And Start Doing".
"Суть успешного стартапа - это превращение идей в действие" и
"Спенс определяет 3 простых ключевых слова, который приведут к совершенству в бизнесе — фокус, дисциплина и действие. Если что-либо из этого у вас отсутствует, на выходе вы получите посредственность. А как только вы начнете мириться с посредственностью, вы станете для нее магнитом."

Потом появилось две статьи на блоге Армена Петросяна "Мыслехранилище №28". Первая о книге Rework и вторая о использовании тегов при сохранении информации "Теги помогают эффективнее действовать". Вот несколько цитат:
"Действовать по обстоятельствам – это нормально. Просто сядьте в самолет и взлетайте. Симпатичную рубашку, крем для бритья и зубную щётку можно купить, уже добравшись до места" (Rework)

"Меня перестала интересовать информация ради информации. Горы не просмотренных ссылок, гигабайты видео, книг и музыки. Действие! Ты живешь когда действуешь, когда проявляешь свой внутренний мир, свои задумки, свои намерения. Действуя эффективно ты максимально используешь имеющиеся возможности."

Конечно эти статьи не только о действиях, но каждый видит то, что хочет видеть, то что может видеть со своей колокольни.

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

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

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

Своими успехами поделюсь в будущих записях.

Всем доброй ночи.

четверг, 7 октября 2010 г.

Мобильная тема

Всем доброго вечера.
Настроил себе мобильный интернет для evernote, а заодно и для блога.

--
Надіслано з мого мобільного пристрою

пятница, 3 сентября 2010 г.

Функция set_new_handler

Нашел интересный метод в С++.
Называется set_new_handler и призван помочь нам в случае если не получилось выделить память.
Описание можно глянуть тут и тут
Ниже пример использования:

#include "stdafx.h"
#include
using namespace std;

void no_memory()
{
printf( "Failed to allocate memory!\n");
throw bad_alloc();
}

int _tmain(int argc, _TCHAR* argv[])
{
set_new_handler(no_memory);
while(1)
{
try
{
int* p = new int[1024*1024*256];
printf("successed\n");
}
catch(bad_alloc b)
{
return 0;
}
}
//delete []p;
return 0;
}

Сразу же возникает закономерный вопрос: а как же его использовать в практическом коде?
А ответ прост - никак.

На проекте может быть два способа работы с памятью:
  • ленивый - все отдается на откуп стандартному new и надеемся, что памяти у пользователя всегда хватит.
  • практичный - команда берет на себя все управление памятью, каждый байт подсчитан и задокументирован.
Начнем со второго случая. Если команда взяла на себя управление памятью, то new переопределен и стало быть метод установленный вызовом set_new_handler никогда не сработает. Да и не нужен он, при нормально написанном менеджере памяти.

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

Доброй всем ночи.

четверг, 5 августа 2010 г.

Final class в C++ 2

Месяц назад написал статью Final class в C++, в которой пытался решить проблему с запретом наследования в С++. Проблема решилась, но при этом возникло два вопроса.

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

С первым все достаточно просто. И звучит он следующим образом: "Почему переопределенные операторы new/delete не имеют доступа к приватным конструктору и деструктору?".
Ответ: потому, что конструктор и деструктор вызываются не из операторов new/delete, их вызывает внешний код, который встраивается компилятором.

Второй вопрос повеселее: "Почему именно виртуальное наследование запрещает наследование от класса объявленного финальным?"
Ответ находится по ссылке С++ Faq. Если по-русски то суть в том, что если в цепочке родителей есть виртуальное наследование, то наследник должен иметь прямой доступ к конструктору данного класса: constructors for virtual base classes anywhere in your class's inheritance hierarchy are called by the "most derived" class's constructor.

Всем доброй ночи.

среда, 7 июля 2010 г.

Final class в С++

Последнее время приходится проводить много собеседований. С одной стороны занимает много времени и сил, а с другой дополнительный стимул расти самому.
Вот недавно всплыл такой вопрос: можно ли запретить наследование от класса в С++. В Java есть такое понятие как final класс, т.е. класс от которого нельзя наследоваться.

Предположим есть некий класс А:

class A
{
public:
A(){}
~A(){}
};

и класс B

class B : public A //все отлично собирается
{
};


Необходимо сделать так, чтобы при попытке наследование компилятор выдавал сообщение об ошибке.

Первое, что приходит в голову это сделать конструктор и деструктор класса А приватными. Действительно после этого я не смогу наследоваться, но возникает другая проблема - я не могу создавать экземпляры класса А.
Пытаемся развить мысль далее. Что если переопределить для класса А операторы new и delete. Так как это будут операторы класса, то они смогут получить доступ к приватным конструктору и деструктору. Получается вот такой класс:

class A
{
private:
A(){}
~A(){}


public:
void* operator new(size_t size)
{
return ::operator new(size);
}

void operator delete(void* block)
{
::operator delete(block);
}
};

сlass B : public A //компилятор ругается
{
};


Но на самом деле это не работает. Переопределенные операторы new\delete все равно не получают доступа к приватным конструктору и деструктору класса А. Объявление операторов new\delete друзьями класса А тоже не помогает.

Второй мыслью (после часа попыток) было полезть во всемирную паутину и найти ответ там. Ответ я нашел, но не на все вопросы. Итак реализация final класса для С++ выглядит следующим образом:

class ClassLocker
{
friend class A;
private:
ClassLocker(){}
};

class A : public virtual ClassLocker
{
public:
A(){}
~A(){}
};

class B : public A //компилятор выдает сообщение об ошибке
{
};

Самое интересное, что если мы убрать виртуальное наследование класса А от класса ClassLocker, т.е. написать class A : public ClassLocker, то все отлично собирается. Слово virtual в данном случае является ключевым.

Итак в итоге есть два вопроса:
  • Почему переопределенные операторы new\delete не имеют доступа к приватном конструктору и деструктору?
  • Почему именно виртуальное наследование запрещает дальнейшее наследование?
Ответы на вопросы я дам в следующей статье.

Всем доброй ночи.

пятница, 11 июня 2010 г.

Блог Джона Роббинса

Читаю книгу Роббинса об отладке приложений. Одна из тех книг, которые стоит прочитать всем. Программистам конечно.

Всем доброй ночи

Problem with delete[]

Рубрика Reference
Буду собирать ссылки на интересные для меня статьи.
delete[] - интересная проблема, возникающая при удалении массива объектов.

Всем доброй ночи

Agile первая попытка

Сегодня составляли спринт бэклог на 2 недели. Потратили 3 часа, но все таки сделали.
Самая большая проблема это заставить людей активно работать. Половина команды согласна работать по тем планам и срокам, которые сделаю я. Не зависимо от того сколько это займет в реальности.
Что это? Нежелание учится чему-то кроме программирования? Отсутствие понимания что все вместе отвечаем за результат? Или же нежелание принимать на себя хоть какую-то ответственность, чтобы потом можно было говорить, мол это не я виноват, это лид дал нереальные сроки?
С другой стороны опыта проведения таких митингов у меня с гулькин нос. Возможно не смог донести важность этой работы? Может сам до конца еще не понял сути Agile и еще нужно учится мне?

Вопросов как всегда море. Будем искать ответы.

Всем доброй ночи.

среда, 2 июня 2010 г.

C++: что лучше учится или учить

Привет всем.

Последние два месяца работаю по принципу: "Глаза боятся, а руки делают".Вот и сейчас такое же ощущение.В нашей компании открываем специальные курсы по обучению С++. Причем курс будет с уклоном в игровую специфику.
Набрали группу из 6 человек, завтра первое занятие.

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

Результатами поделюсь позже.

Всем доброй ночи.

пятница, 23 апреля 2010 г.

Полезные ссылки

Недавно нашел подборку ссылок на поисковики для иконок. Иногда бывает очень полезно, особенно когда сам рисовать не умеешь. :)
Вот ссылка на исходную статью.
А вот ссылки на поисковики:
- Free Icon Search Engine
- Icon Finder

Идем дальше: следующая ссылка на сайт MSDN. Никогда особенно MSDN не увлекался. Пожалуй стоит время от времени заглядывать сюда.

Всем доброй ночи

понедельник, 29 марта 2010 г.

2D animation system мысли вслух

    В этой статье я попробую описать идеи по поводу создания анимационной 2D системы, которые у меня возникли за несколько лет работы.

    Во-первых, нужно определиться с требованиями к системе:
  • Система должна быть построена так, чтобы минимизировать дублирование данных, особенно графических.
  • Нужно минимизировать количество связей системы с внешним миром или хотя бы локализовать точи соприкосновения и привести их к общему интерфейсу.
  • Система должна уметь работать с анимацией как зашитой в один файл, так и набором изображений.
  • Нужна возможность проигрывать часть анимации, например с 1 по 10 кадр, либо только 5 кадр.
  • Нужна возможность задавать для одной и той же анимации различные свойства, например в одном месте зациклить анимацию, а в другом она должна проиграться все один раз, в третьем она должна играться в обратном направлении.
  • Хорошо было бы добавлять на анимацию различные действия, например проигрывать звук на определенном кадре анимации, или установить какой-то флаг на нужном нам кадре анимации.
    Теперь перейдем непосредственно к реализации системы. Ниже приведена диаграмма классов, к которой я пришел в конце концов и которая, как мне кажется, вполне отвечает требованиям перечисленным выше.



    Получилось даже некоторое подобие MVC (Model View Controller) структуры.
    В качестве контроллера выступает интерфейс Player. Он предоставляет интерфейс, который позволяет контролировать проигрывание анимации, и каждый кадр Player выдает структуру FrameData, которой вполне достаточно, чтобы нарисовать текущий кадр анимации.
    Но рисовать-то как раз Player и не умеет, эта задача полностью лежит на AnimationDriver. Данный класс представляет View в моей схеме. Он умеет рисовать кадр анимации, и также он хранит настройки материала, с которым будет применена текстура анимации.
    Последней составляющей системы является класс Entity2D. Он соединяет воедино все элементы системы и одновременно является моделью (Model). Класс содержит данные о трансформации объекта, его положении в мире.

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

    AnimationPlayer - единственный проигрыватель, который имеет доступ к ресурсу анимации. Он умеет проигрывать анимацию с заданной скоростью от первого и до последнего кадра. Также у него можно запросить количество кадров в анимации и данные FrameData о каждом кадре.

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

    ChooserPlayer - данный проигрыватель содержит список проигрывателей и по заданному условию может выбирать одну из них. Разновидностью этого проигрывателя является RandomChooserPlayer, который случайным образом выбирает анимацию для проигрывания.

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

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

Всем доброй ночи.

понедельник, 22 марта 2010 г.

проверка связи

Привет всем.

Это тестовое сообщение, проверка связи.
Я настроил отправку записей в блог через почту.

Теперь хочу посмотреть на результат.

Доброй всем ночи.

среда, 10 февраля 2010 г.

Lets start

Случайно нашел в интернете лекции по программированию: http://www.youtube.com/watch?v=Ps8jOj7diA0&feature=PlayList&p=66CEBF7333101C6E&index=0

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

ForestMan