Книга Майкла Физерса «Эффективная работа с унаследованным кодом»
Пишите код так, будто человек,
который будет его поддерживать – маньяк-психопат,
который знает, где вы живете.
Цитата из книги С. Макконнелла «Совершенный код»
Везет тем программистам, которые начинают вести проекты с нуля, тогда можно развернуться по-полной и писать код так, как считаешь нужным — cначала тщательно спроектировать структуру, нарисовать классы в виде UML-диаграмм и неспешно приступить к кодированию с использованием принципа TDD (Test-driven development), когда сначала пишутся тесты, а потом уже начинают писать основной код. Но чаще программистов нанимают для работы над уже существующей программой, которую до тебя писал криворукий программер (ведь код, написанный не тобой по определению считается плохим) без тестов, с непонятными (тебе) хаками, ужасным наименованием переменных, да и вообще читать этот код невозможно, и лучше его переписать с нуля (см. первую фразу). Такой код называют унаследованный, хотя его автор вполне может быть жив-здоров.
Правда, даже если проект начинался с нуля, то через некоторое время (если проект тогда еще будет на плаву) его код может стать унаследованным для других программистов, да даже и для тебя самого. А во всем виноваты заказчики, которые сами не знают чего хотят (а их кто-нибудь об этом спрашивал?), постоянно меняют требования (ну не нравятся им фиолетовые кнопки в правом углу на розовом фоне), хотят получить еще вот эту «маленькую возможность», из-за которой приходится переделывать половину программы. А время то поджимает, тесты писать уже некогда, слово «рефакторинг» произносится с содроганием, возможности добавляются с помощью великого копипаста, а о том, что половина имен классов уже не соответствует их настоящему поведению уж и не говорится.
Если вам знакома такая ситуация, то книга Физерса «Эффективная работа с унаследованным кодом» для вас будет полезна. Она посвящена тому, что делать, если надо работать с кодом, который написан небрежно и не покрыт полностью тестами. Основная ситуация, описываемая Физерсом — надо добавить новую возможность или изменить поведенение кода, который не то что страшно трогать, но даже иногда непонятно, как он вообще работает.
Майкл Физерс (Michael Feathers) является автором таких известных инструментов unit-тестирования, как CppUnit и CppUnitLite, и для него в коде главное — это его тестируемость. С его точки зрения код является достаточно хорошим, если его можно протестировать с помощью unit-тестов, причем именно с помощью тестов, которые выполняются очень быстро, чтобы программистам не было лень их запускать после каждой правки кода. Так, Физерс считает, что каждый unit-тест должен выполняться не более 1/10 секунды, больше могут выполняться приемочные и интеграционные тесты, которые проверяют работоспособность программы в целом, а именно unit-тесты, которые проверяют работоспособность отдельных классов должны выполняться очень быстро, а это значит, что они не должны обращаться к базам данных, получать данные из сети и т.п.
Основная идея книги — как провести рефакторинг кода таким образом, чтобы его можно было бы в дальнейшем изменять, добавляя новые возможности или изменяя поведение. Большинство советов заключаются в том, как разорвать зависимости между классами для того, чтобы в тестах не приходилось бы создавать десятки тяжелых классов для того, чтобы протестировать один из них. Особенно это актуально для тестирования классов, связанных с пользовательским интерфейсом (GUI) и обращением к базам данных. При этом автор честно пишет, что в результате таких изменений структура программы может даже ухудшиться, например, некоторые методы класса, которые раньше были приватными (private) становятся защищенными (protected) и то и публичными (public), часто вводятся классы, от которых можно будет избавиться, когда (если) дойдет дело до глобального рефакторинга кода, а так они будут маячить, как бельмо в глазу, напоминая программистам, что здесь нужна глобальная переделка кода. Так что главный смысл книги заключается не в том, как сделать структуру программы более правильной, а изменить плохую структуру так, чтобы классы можно было бы загнать в среду для unit-тестирования.
При этом автор в начале книги вводит такое понятие, как «шов». Швом он называет такие места, куда можно внедрить свой код во время тестирования. Самый безопасный шов — это объектный, когда можно сделать производный класс, который бы не зависел от некоторых других классов (при этом часто приходится из него выделять интерфейс). Другим примером могут служить объекты-подделки (fake- или mock-объекты), которые имеют интерфейс некоторых классов, но при этом, например, не тянут за собой большой библиотеки.
Не брезгует автор пользоваться и препроцессором C++ (примеры в книге написаны в основном на C++ или Java, хотя есть примеры на C# и даже один на Ruby), чтобы подменять поведение классов в тестах по сравнению с конечным кодом.
В целом книга интересная и полезная, не многие книги заставляют посмотреть на свой код под другим углом зрения, иногда пренебрегая казалось бы прописными истинами объектно-ориентированного программирования (например, что инкапсуляция — это всегда хорошо). В начале книга читается очень легко, правда, под конец уже теряешь разницу между примерами, на которых автор показывает приемы для ввода тестов, и кажется, что читаешь примерно одно и то же под разными углами. Правда, Физерс в начале пишет, что при написании книги, он рассчитывал, что ее будут читать, перескакивая с главы на главу, а не читать от корки до корки. Да и названия приемов получились какие-то слишком абстрактными, по их названию в оглавлению трудно понять, что именно тебе нужно в данным момент, уж не знаю, может быть в этом отчасти виноват перевод.
Кстати, на счет перевода. В целом он довольно хороший, например, в книгах не часто встречается перевод названий классов (разумеется, в коде все классы именуются на английском языке, но в тексте при первом упоминании классов часто указывается их перевод, чтобы было понятно, что они делают). Мелочь, но приятная.
Хотя при переводе вышли некоторые косяки. Самый заметный — это то, что широко известную в узких кругах ученую в области вычислительных систем Барбару Лисков сделали мужиком. В объектно-ориентированном программировании есть такое понятие, как принцип подстановки Лисков, который по-английски звучит как «Liskov substitution principle». Переводчик, видно, решил, что Liskov — это он, и перевел этот принцип, как принцип подстановки Лискова (а может тут и редакторы «помогли»).
А так книжка хорошая, прочитать ее стоит.
PS. В основном в подобных книгах про проектирование и рефакторинг используются компилируемые языки со строгой типизацией, а интересно было бы почитать что-нибудь похожее применительно к какому-нибудь динамическому языку, особенно к моему любимому Python. Ведь там с одной стороны больше возможностей для архитектурных ухищрений, а с другой — компилятор там уже не помощник для поиска мест, где нужно внести исправления в процессе рефакторинга.
PPS. Фотография Физерса взята отсюда.
PS. Вы можете подписаться на новости сайта через RSS, Группу Вконтакте или Канал в Telegram.
Денис Сепетов:
Маловероятно, что книгу прочитаю (для меня пока не актуально), но цитата про маньяка-психопата меня зацепила 🙂 Суровая правда
21 января 2012, 9:49 пп