XP практики в проектах
с тяжелой наследственностью



               Oleg Klymenko
               Java Developer @ Sigma Ukraine

               oklym@meta.ua
Начало
Развитие
Agile-манифест разработки программного обеспечения

  Мы постоянно открываем для себя более совершенные методы
  разработки программного обеспечения, занимаясь разработкой
           непосредственно и помогая в этом другим.
     Благодаря проделанной работе мы смогли осознать, что:

     Люди и взаимодействие важнее процессов и инструментов
    Работающий продукт важнее исчерпывающей документации
    Сотрудничество с заказчиком важнее согласования контракта
Готовность к изменениям важнее следования первоначальному плану

          То есть, не отрицая важности того, что справа,
            мы всѐ таки больше ценим то, что слева.

                                                           Идея
Процесс
Ожидание
Реальность
Демотивация
Первый шаг
Анализ

1. Страсть (избыток проектирования)
2. Чревоугодие (неспособность к рефакторингу)
3. Жадность (соревнование между командами разработки)
4. Лень (отсутствие проверки входных данных)
5. Гнев (отсутствие практики комментировать код)
6. Зависть (не использование систем управления версиями)
7. Гордость (отсутствие юнит-тестирования)

                                             Neil McAllister
8. Оптимизм
9.
10.
Новая фича
Новая фича
Тест разработчика
$




         Вызов


    DT
1.   Я пишу пользовательский интерфес
2.   Трудно сопровождать
3.   Они не ловят новые “баги”
4.   Они медленные
5.   Это скучно
6.   Это дело тест-тима
7.   У меня легаси код
8.   Мне нужно делать фичи
9.

                                        Отговорки
public class Validator {
        private static Validator instance =
                               new Validator(FeatureFactory.get());
        private Subscription subscr;
        private final Player player;

       public static Validator getInstance() {return instance;}

       private Validator(FeatureFactory ff) {
               player = ff.getPlayer();
               if (player instanceof GamePlayer)
                       subscr = ff.findSubscription(player);
       }

       public boolean check(Round round) {
               SyncService sync = Repository.lookup(SyncService.class);
               boolean result = false;
               Date time = new Date();
               if(!round.isActiveInTime(time)) {
                       try {
                               sync.lock(round);
                               Ticket ticket = new SubscriptionTicket(subscr);
                               result = round.subscribe(ticket, player);
                       }
                       finally {
                               sync.unlock(round);
                       }
               }
               return result;
       }
}
public class Validator {
        private static Validator instance =
                               new Validator(FeatureFactory.get());
        private Subscription subscr;
        private final Player player;

       public static Validator getInstance() {return instance;}

       private Validator(FeatureFactory ff) {
               player = ff.getPlayer();
               if (player instanceof GamePlayer)
                       subscr = ff.findSubscription(player);
       }

       public boolean check(Round round) {
               SyncService sync = Repository.lookup(SyncService.class);
               boolean result = false;
               Date time = new Date();
               if(!round.isActiveInTime(time)) {
                       try {
                               sync.lock(round);
                               Ticket ticket = new SubscriptionTicket(subscr);
                               result = round.subscribe(ticket, player);
                       }
                       finally {
                               sync.unlock(round);
                       }
               }
               return result;
       }
}
public class V {
        private static V instance =
                               new V(FF.get());
        private S s;
        private final P p;

       public static V getInstance() {return instance;}

       private V(FF ff) {
               p = ff.getP();
               if (player instanceof GamePlayer)
                       s = ff.findS(player);
       }

       public boolean do(R r) {
               SS sync = Repository.lookup(SS.class);
               boolean result = false;
               Date time = new Date();
               if(!round.timeDependentRoutine(time)) {
                       try {
                               sync.lock(round);
                               T t = new ST(s);
                               result = round.s(t, player);
                       }
                       finally {
                               sync.unlock(round);
                       }
               }
               return result;
       }
}
public class Validator {
        private final Subscription subscr;
        private final Player player;
        private final SyncService sync;

       public Validator(Player player,
                       Subscription subscr, SyncService sync) {
               this.player = player;
               this.subscr = subscr;
               this.sync = sync;
       }

       public boolean check(Round round, Date time) {
               boolean result = false;
               if(!round.isActiveInTime(time)) {
                       try {
                               sync.lock(round);
                               Ticket ticket = new SubscriptionTicket(subscr);
                               result = round.subscribe(ticket, player);
                       }
                       finally {
                               sync.unlock(round);
                       }
               }
               return result;
       }
}
1.   Смешивание инстанциирования с логикой
2.   Смешивание патерна Lookup с логикой
3.   Выполнение целевой логики в конструкторе
4.   Глобальная видимость полей класса
5.   Использование патерна Singleton
6.   Статические методы
7.   Глубокая иерархия наследования
8.   Смешивание сервисов с общей логикой


                                      Антипатерн
1.   Сбалансированный ООП дизайн.
   2.   Внедрение внешних зависимостей (DI).
   3.   Отслеживание ошибок смешивания логики.
   4.   Соблюдение закона Деметры.
   5.   Юнит тесты (TDD).




Тестируемость
Норма



Системные тесты




Функциональные и интеграционные тесты



Unit тесты
Переворот
Время
Человеческий фактор
Архитектура и Дизайн


           Комментарии
                                                  Дублирование
                                Исходный              кода
            Стандарты              код
           кодирования                            Юнит тесты


                         Потенциальные      Сложность
                            ошибки



Контроль
Итоговый план


1.   Налаживаем сборку/инсталяцию.
2.   Определяем поведенческие требования.
3.   Создаем покрытие функциональными тестами.
4.   Выполняем рефакторинг.
5.   Покрываем юнит тестами.
6.   Налаживаем инспекцию кода.
7.   Убираем излишние функциональные тесты.
8.   Оставляем мир лучше чем был до нас :)
Пять минут...
1. Miško Hevery. Writing Testable Code
   http://misko.hevery.com/code-reviewers-guide/

2. Wiktor Żołnowski. Reversed Tests Pyramid
   http://xpdays.com.ua/materials/legacy-code/

3. Neil McAllister. 7 deadly sins of software development
   http://gigaom.com/2012/06/02/the-7-deadly-sins-of-
software-development/
XP практики в проектах с тяжелой наследственностью

XP практики в проектах с тяжелой наследственностью

  • 1.
    XP практики впроектах с тяжелой наследственностью Oleg Klymenko Java Developer @ Sigma Ukraine oklym@meta.ua
  • 2.
  • 3.
  • 4.
    Agile-манифест разработки программногообеспечения Мы постоянно открываем для себя более совершенные методы разработки программного обеспечения, занимаясь разработкой непосредственно и помогая в этом другим. Благодаря проделанной работе мы смогли осознать, что: Люди и взаимодействие важнее процессов и инструментов Работающий продукт важнее исчерпывающей документации Сотрудничество с заказчиком важнее согласования контракта Готовность к изменениям важнее следования первоначальному плану То есть, не отрицая важности того, что справа, мы всѐ таки больше ценим то, что слева. Идея
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
    Анализ 1. Страсть (избытокпроектирования) 2. Чревоугодие (неспособность к рефакторингу) 3. Жадность (соревнование между командами разработки) 4. Лень (отсутствие проверки входных данных) 5. Гнев (отсутствие практики комментировать код) 6. Зависть (не использование систем управления версиями) 7. Гордость (отсутствие юнит-тестирования) Neil McAllister 8. Оптимизм 9. 10.
  • 11.
  • 12.
  • 13.
  • 14.
    $ Вызов DT
  • 15.
    1. Я пишу пользовательский интерфес 2. Трудно сопровождать 3. Они не ловят новые “баги” 4. Они медленные 5. Это скучно 6. Это дело тест-тима 7. У меня легаси код 8. Мне нужно делать фичи 9. Отговорки
  • 16.
    public class Validator{ private static Validator instance = new Validator(FeatureFactory.get()); private Subscription subscr; private final Player player; public static Validator getInstance() {return instance;} private Validator(FeatureFactory ff) { player = ff.getPlayer(); if (player instanceof GamePlayer) subscr = ff.findSubscription(player); } public boolean check(Round round) { SyncService sync = Repository.lookup(SyncService.class); boolean result = false; Date time = new Date(); if(!round.isActiveInTime(time)) { try { sync.lock(round); Ticket ticket = new SubscriptionTicket(subscr); result = round.subscribe(ticket, player); } finally { sync.unlock(round); } } return result; } }
  • 17.
    public class Validator{ private static Validator instance = new Validator(FeatureFactory.get()); private Subscription subscr; private final Player player; public static Validator getInstance() {return instance;} private Validator(FeatureFactory ff) { player = ff.getPlayer(); if (player instanceof GamePlayer) subscr = ff.findSubscription(player); } public boolean check(Round round) { SyncService sync = Repository.lookup(SyncService.class); boolean result = false; Date time = new Date(); if(!round.isActiveInTime(time)) { try { sync.lock(round); Ticket ticket = new SubscriptionTicket(subscr); result = round.subscribe(ticket, player); } finally { sync.unlock(round); } } return result; } }
  • 18.
    public class V{ private static V instance = new V(FF.get()); private S s; private final P p; public static V getInstance() {return instance;} private V(FF ff) { p = ff.getP(); if (player instanceof GamePlayer) s = ff.findS(player); } public boolean do(R r) { SS sync = Repository.lookup(SS.class); boolean result = false; Date time = new Date(); if(!round.timeDependentRoutine(time)) { try { sync.lock(round); T t = new ST(s); result = round.s(t, player); } finally { sync.unlock(round); } } return result; } }
  • 19.
    public class Validator{ private final Subscription subscr; private final Player player; private final SyncService sync; public Validator(Player player, Subscription subscr, SyncService sync) { this.player = player; this.subscr = subscr; this.sync = sync; } public boolean check(Round round, Date time) { boolean result = false; if(!round.isActiveInTime(time)) { try { sync.lock(round); Ticket ticket = new SubscriptionTicket(subscr); result = round.subscribe(ticket, player); } finally { sync.unlock(round); } } return result; } }
  • 20.
    1. Смешивание инстанциирования с логикой 2. Смешивание патерна Lookup с логикой 3. Выполнение целевой логики в конструкторе 4. Глобальная видимость полей класса 5. Использование патерна Singleton 6. Статические методы 7. Глубокая иерархия наследования 8. Смешивание сервисов с общей логикой Антипатерн
  • 21.
    1. Сбалансированный ООП дизайн. 2. Внедрение внешних зависимостей (DI). 3. Отслеживание ошибок смешивания логики. 4. Соблюдение закона Деметры. 5. Юнит тесты (TDD). Тестируемость
  • 22.
    Норма Системные тесты Функциональные иинтеграционные тесты Unit тесты
  • 23.
  • 24.
  • 25.
  • 26.
    Архитектура и Дизайн Комментарии Дублирование Исходный кода Стандарты код кодирования Юнит тесты Потенциальные Сложность ошибки Контроль
  • 27.
    Итоговый план 1. Налаживаем сборку/инсталяцию. 2. Определяем поведенческие требования. 3. Создаем покрытие функциональными тестами. 4. Выполняем рефакторинг. 5. Покрываем юнит тестами. 6. Налаживаем инспекцию кода. 7. Убираем излишние функциональные тесты. 8. Оставляем мир лучше чем был до нас :)
  • 28.
  • 29.
    1. Miško Hevery.Writing Testable Code http://misko.hevery.com/code-reviewers-guide/ 2. Wiktor Żołnowski. Reversed Tests Pyramid http://xpdays.com.ua/materials/legacy-code/ 3. Neil McAllister. 7 deadly sins of software development http://gigaom.com/2012/06/02/the-7-deadly-sins-of- software-development/

Editor's Notes

  • #6 Отсутствие прозрачных процедур сборки и установкиНе структурированный кодОтсутсвие тестов, как следствие не тестируемый код
  • #7 1. билд в один клик2. деплой в один клик3. тест-тим строит и деплоит IR
  • #9 Разработчик не ведает что творит (только вот эта маленькая фича и она ничего не сломает)
  • #10  Но иногда ведаем что творим, но приходится идти осознано
  • #14 Что именно делает код — не важно. Для тестирования важно только как код структурирован.
  • #15 Что именно делает код — не важно. Для тестирования важно только как код структурирован.
  • #16 Что именно делает код — не важно. Для тестирования важно только как код структурирован.
  • #17 Что именно делает код — не важно. Для тестирования важно только как код структурирован.
  • #21 1. Unit тестирование без модулей2. Интеграционное когда нечего интегрировать3. Не возможно рефакторить без тестов4. Системные тесты дают нам уверенность
  • #22 1. Системные тесты сложны в поддержке2. Выполняются долго и редко3. Переворачиваем пирамиду