|
Программирование >> Разработка устойчивых систем
См.: Бек К., Фаулер М. Экстремальное программирование: планирование. Библиотека программиста. СПб.: Питер, 2003; и Ауэр К., Миллер Р. Экстремальное программирование: постановка процесса. С первых шагов и до победного конца . СПб.: Питер, 2004. - Примеч. перев. ших преимуществ объектно-ориентированных технологий, но для нее необходимы творчески мыслящие программисты, способные создавать гибкий код. Изменяться всегда трудно. Другой импульс к изменению дает сам программист. Художник внутри вас хочет постоянно соверщенствовать архитектуру ващих программ. Какой программист, которому доводилось заниматься сопровождением чужих творений, не проклинал устаревающий продукт, превратившийся в сплошной клубок запутанной логики с навешанными там и сям заплатками? Однако руководство предпочитает, чтобы вы даже не пытались совершенствовать кое-как работающую систему, и это мешает вам внести в код столь нужную гибкость. Известное правило Не сломано - не чини постепенно заменяется другим: Чинить бесполезно - проще переписать . Изменения неизбежны. К счастью, наша отрасль постепенно привыкает к понятию переработки - искусству внутренней реструктуризации кода с целью улучшения его архитектуры без изменения его поведения. К числу таких усовершенствований относится выделение одной функции из другой или наоборот, объединение функций; замена функции класса объектом; параметризация отдельных функций или целых классов, а также замена условных проверок полиморфизмом. Переработка способствует эволюции программ. Откуда бы ни исходило стремление к изменениям, от пользователей или программистов, сегодняшние модификации могут нарушить то, что еще вчера успешно работало. Нам нужна методика построения программ, адаптирующихся к изменениям и совершенствующихся с течением времени. Экстремальное программирование (eXtreme Programming, ХР) - всего лишь одна из многих вариаций на тему ускорения разработки программного обеспечения. В этом разделе мы рассмотрим то, что, как нам кажется, является одним из ключевых факторов гибкой, последовательной разработки: простую и удобную систему автоматизации модульного тестирования. (Впрочем, тестеры - профессионалы, зарабатывающие на жизнь тестированием чужих программ - по-прежнему незаменимы. Здесь всего лишь описывается методика, которая помогает разработчикам писать более качественные программы.) Разработчики пишут модульные тесты, чтобы с уверенностью сделать два самых важных заявления в работе любого программиста: я понимаю предъявленные требования; моя программа (насколько мне известно) соответствует этим требованиям. Лучший способ убедиться в том, что вы правильно понимаете, как должна работать программа - начать с написания модульных тестов. Это простое упражнение поможет вам сосредоточиться на предстоящей работе и, скорее всего, быстрее приведет к конечному результату, чем если вы просто возьметесь за программирование. В терминологии ХР это выражается так: тестирование + программирование быстрее, чем просто программирование Предварительное написание тестов также помогает защититься от фаничных условий, способных нарушить работу программы, так что программа становится более надежной. Если программа прошла все тесты, вы знаете, что в случае неработоспособности системы причина, скорее всего, кроется не в вашем коде. Заявление Все тесты проходят нормально - весьма убедительный аргумент. Автоматизация тестирования Как же выглядит модульный тест? Слишком часто разработчики берут хорошие входные данные, получают ожидаемые результаты и бегло просматривают их. У такого подхода есть два недостатка. Прежде всего, программы не всегда получают хорошие данные. Все знают, что входные данные нужно протестировать по граничным условиям, но об этом трудно думать, когда вы просто добиваетесь хоть какой-то работоспособности программы. Если написать тест для функции перед тем, как браться за ее программирование, вы сможете представить себя на месте тестера и спросить себя: Как бы это сломать? Напишите тест, который бы доказывал, что программируемая вами функция действительно работает, а потом снова перевоплощайтесь в программиста и пишите саму функцию. Программа получится более качественной, чем без предварительного написания теста. Вторая опасность состоит в том, что визуальный просмотр выходных данных утомителен и чреват ошибками. Компьютер способен взять на себя большую часть нетворческой работы, выполняемой человеком, но без присущих человеку ошибок. Лучше сформулировать тесты в виде набора логических выражений и заставить тестовую программу сообщить обо всех нарушениях. Предположим, вы создаете класс Date, который должен обладать следующими свойствами: дата может инициализироваться строкой (ГГГГММДД), тремя целыми числами (Г, М, Д) или ничем (для текущей даты); объект даты по запросу возвращает год, месяц, день или строку в формате ГГГГММДД ; класс поддерживает все операции сравнения, а также вычисление промежутков между двумя датами (в годах, месяцах и днях); сравниваемые даты могут быть разделены произвольным количеством веков (например, 1600-2200). Класс содержит три целочисленные переменные для хранения года, месяца и дня (проследите за тем, чтобы год представлялся минимум 16 битами для выполнения последнего пункта в списке). Интерфейс класса Date выглядит примерно так: : C02:Datel.h Первая версия Date.h #ifndef DATE1 H #define DATE1 H #i nclude <str1ng> class Date { public: int mainO { Date mybday(1951. 10. 1); test(mybday.getYearO == 1951); test(mybday.getMonthO == 10); test(mybday.getDay() ==1): cout Passed: nPass . Failed: nFai1 endl: /* Ожидаемый вывод: Passed: 3. Failed: 0 */ III:- В этом тривиальном случае функция test() содержит глобальные переменные nPass и nFail. Вся визуальная проверка сводится к чтению результатов тестирования. Если тест завершился неудачей, более сложная версия test() выводит соответствующее сообщение. В системе, описанной далее в этой главе, среди прочего будет присутствовать такая тестовая функция. Структура для представления промежутков времени struct Duration { int years: int months: int days: Durationdnt y. int m. int d) : years(y), months(m) .days(d) {} Date(): Datednt year, int month, int day): Date(const std::string&): int getYearO const: int getMonthO const: int getDayO const; std::string toStringO const; friend bool operator<(const DateS, const DateS): friend bool operator>(const DateS. const DateS): friend bool operator<=(const DateS. const DateS): friend bool operator>=(const DateS. const DateS): friend bool operator==(const DateS. const DateS): friend bool operator!=(const DateS. const DateS): friend Duration duration(const DateS. const DateS): lendif DATE1 H /:- Прежде чем браться за реализацию класса, стоит укрепить свое понимание требований и написать прототип тестовой программы. Вероятно, эта программа могла бы выглядеть примерно так: : C02;SimpleDateTest.cpp {L} Date finclude <iostream> #include Date.h Из приложения using namespace std: Механизм тестирования int nPass = 0. nFail = 0; void test(bool t) { if(t) nPass++: else nFail++:
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |