вам контролировать действие, интенсивность которого стала слишком большой.

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

Приложение II

Фибоначчи

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

Первый тест показывает, что fib(0) = 0. Реализация возвращает константу.

public void testFibonacci() {

assertEquals(0, fib(0));

}

int fib(int n) {

return 0;

}

(Я использую класс TestCase как вместилище кода, так как мы разрабатываем всего одну функцию.)

Второй тест показывает, что fib(1) = 1.

public void testFibonacci() {

assertEquals(0, fib(0));

assertEquals(1, fib(1));

}

Я просто добавил еще один оператор assert() в тот же самый тестовый метод, так как не вижу особого смысла создавать новый метод с именем testFibonacciOfOneIsOne.

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

int fib(int n) {

if (n == 0) return 0;

return 1;

}

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

public void testFibonacci() {

int cases[][] = {{0,0},{1,1}};

for (int i = 0; i < cases.length; i++)

assertEquals(cases[i][1], fib(cases[i][0]));

}

Теперь добавление нового теста требует всего шесть нажатий на клавиши и никаких дополнительных строк:

public void testFibonacci() {

int cases[][] = {{0,0},{1,1},{2,1}};

for (int i = 0; i < cases.length; i++)

assertEquals(cases[i][1], fib(cases[i][0]));

}

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

public void testFibonacci() {

int cases[][] = {{0,0},{1,1},{2,1},{3,2}};

for (int i = 0; i < cases.length; i++)

assertEquals(cases[i][1], fib(cases[i][0]));

}

Ура! Наконец-то тест не сработал. Воспользуемся прежней стратегией (рассматриваем меньшие входные значения как специальные случаи):

int fib(int n) {

if (n == 0) return 0;

if (n <= 2) return 1;

return 2;

}

Теперь мы готовы обобщить код. Мы написали 2, однако на самом деле мы имели в виду 1 + 1.

int fib(int n) {

if (n == 0) return 0;

if (n <= 2) return 1;

return 1 + 1;

}

Первая единица в сумме – на самом деле fib(n-1):

int fib(int n) {

if (n == 0) return 0;

if (n <= 2) return 1;

return fib(n-1) + 1;

}

Вторая единица в сумме – на самом деле fib(n-2):

int fib(int n) {

if (n == 0) return 0;

if (n <= 2) return 1;

return fib(n-1) + fib(n-2);

}

Теперь окончательно чистим код. Та же самая структура должна работать для fib(2), поэтому мы можем преобразовать второй условный оператор:

int fib(int n) {

if (n == 0) return 0;

if (n == 1) return 1;

return fib(n-1) + fib(n-2);

}

Это и есть функция вычисления последовательности Фибоначчи, целиком и полностью разработанная в рамках методики TDD.

Послесловие

Мартин Фаулер (Martin Fowler)

Когда рассказываешь о разработке, основанной на тестировании, сложнее всего передать то психическое состояние, в котором находишься, работая в стиле TDD. Я помню, как в ходе проекта C3 мы с Ральфом Битти (Ralph Beattie) работали над реализацией сложного набора условий выплаты. Ральф сформулировал набор соответствующих тестов, после чего мы приступили к реализации этих тестов одного за другим. Процесс был равномерным и неторопливым, из-за этого казалось, что мы работаем медленно. Однако, взглянув назад на проделанную работу, можно было понять, что, несмотря на кажущуюся неторопливость, мы работали очень даже быстро.

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

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

Добавление новой функциональности при помощи тестов и рефакторинг – это две монологические разновидности программирования. Совсем недавно я открыл еще одну разновидность: копирование шаблона. Я занимался разработкой сценария на языке Ruby, извлекающего информацию из базы данных. Я начал с создания класса, являющегося оболочкой таблицы базы данных, а затем сказал себе, что, раз я только что закончил книгу о шаблонах работы с базами данных, я должен использовать шаблон. Примеры программ в книге были написаны на Java, поэтому нужный мне код легко можно было перенести на Ruby. Когда я программировал, я не думал о решении проблемы, я думал лишь о том, как лучше всего адаптировать шаблон для условий, в рамках которых я работал.

Копирование шаблонов само по себе не является хорошим программированием, – я всегда подчеркиваю этот факт, когда говорю о шаблонах. Любой шаблон – это полуфабрикат, – вы должны адаптировать его для условий своего проекта. Однако чтобы сделать это, лучше всего вначале, особо не задумываясь, скопировать шаблон, а затем, воспользовавшись смесью рефакторинга и TDD, выполнить адаптацию. В этом случае в процессе копирования шаблона вы также концентрируетесь только на одной вещи – на шаблоне.

Сообщество XP активно работает над добавлением шаблонов в общую картину. Со всей очевидностью можно сказать, что сообщество XP любит шаблоны. В конце концов, между множеством приверженцев XP и множеством приверженцев шаблонов существует значительное пересечение: Уорд и Кент являются лидерами обоих направлений. Наверное, копирование шаблона – это третий монологический режим программирования наряду с разработкой в стиле «сначала тесты» и рефакторингом. Как и первые два режима, копирование шаблона – опасная штука, если ее использовать отдельно от двух других режимов. Все три вида программирования проявляют свою мощь только тогда, когда используются совместно друг с другом.

Если вы хотите сделать некоторый процесс эффективным, вы должны идентифицировать

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

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

Отметить Добавить цитату