97 этюдов для программистов. Опыт ведущих экспертов - Пит Гудлиф
Рассмотрим простой интернет-магазин, который принимает к оплате только кредитные карты и не выписывает клиентам счета. При этом класс Order (заказ) содержит такой метод:
public boolean isComplete() {
return isPaid() && hasShipped();
}
Разумно, так? Так вот, хоть это выражение и выделено в аккуратный метод, а не разбросано по всему коду путем копирования и вставки, его вообще не должно существовать. А то, что оно существует, указывает на проблему. Какую? Нельзя доставить заказ, прежде чем он оплачен. Поэтому hasShipped не может вернуть true, пока isPaid не вернет true, а в этом случае часть выражения избыточна. Возможно, вам все же нужен метод isComplete для ясности кода, но тогда он должен выглядеть как-то так:
public boolean isComplete() {
return hasShipped();
}
В своей работе я постоянно сталкиваюсь и с недостающими, и с избыточными проверками. Я привел крошечный пример, но если добавить сюда отмену заказа и возврат денег, дело усложняется, и потребность в правильной обработке состояний возрастает. В данном случае заказ может находиться только в одном из трех разных состояний:
• В процессе: можно добавлять и удалять товары. Нельзя осуществлять доставку.
• Оплачен: нельзя добавлять и удалять товары. Можно доставлять.
• Доставлен: конец. Больше никаких изменений.
Наличие этих состояний важно, и перед выполнением операций следует проверять, что вы в нужном состоянии, а также проверять, что из текущего состояния вы перейдете в допустимое. Короче, нужно защищать объекты тщательно и в правильных местах.
Но как начать думать состояниями? Выделить выражения в осмысленные методы — очень хорошее начало, но это лишь начало. Главное — понимать конечные автоматы. Знаю, у вас сохранились неприятные воспоминания о них из курса информатики, но оставьте это в прошлом. Конечные автоматы — это не особенно сложно. Рисуйте их, тогда их легче понять и обсуждать. Запускайте свой код в тестовом режиме, чтобы разделить допустимые и недопустимые состояния, а также переходы между ними, и поддерживать их корректность. Изучите шаблон State. Освоившись с ним, почитайте о контрактном программировании (design by contract). Оно помогает обеспечить допустимость состояния путем проверки данных и самого объекта на входе и выходе из каждого открытого метода.
Если состояние некорректно, значит, в коде ошибка, и вы рискуете потерять данные, если не прервете выполнение. Если вам кажется, что проверки состояний замусоривают код, научитесь скрывать их с помощью специальных инструментов, генерации кода, вплетений (weaving) или аспектов. Независимо от того, какой метод вы выберете, подход на основе состояний сделает ваш код более простым и надежным.
Одна голова хорошо, но две — часто лучше
Эдриан Уайбл
Программирование требует вдумчивости, а вдумчивость может существовать только в уединении. Такой стереотип присущ многим программистам.
Но стиль «одинокого волка» в программировании уже давно отступает перед командным подходом, который, смею утверждать, улучшает качество работы, повышает производительность и позволяет разработчикам получать большее удовольствие от работы. Новый подход заставляет разработчиков работать друг с другом теснее, а также работать с теми, кто не участвует в разработке, — системными и бизнес-аналитиками, специалистами в области контроля качества и пользователями.
Что это означает для разработчиков? Уже недостаточно быть экспертом в области технологии программирования. Вы должны научиться эффективно работать с другими людьми.
Сотрудничество на работе — это не игра в вопросы и ответы и не долгие совещания. Сотрудничество — это засучить рукава вдвоем с коллегой и приступить к трудной задаче.
Я большой поклонник парного программирования. Можно назвать это «экстремальным сотрудничеством». Когда я работаю в паре, мое мастерство программиста растет. Если я слабее своего партнера в предметной области или в какой-либо технологии, то просто учусь на его опыте. Когда я сильнее в каком-то аспекте, то начинаю лучше понимать, что я знаю и чего не знаю, поскольку мне приходится давать объяснения. В любом случае мы оба вносим свой вклад и учимся друг у друга.
Работая в паре, мы привносим свой коллективный опыт — как опыт в предметной области, так и технический — для решения стоящей перед нами задачи. Вместе мы способны предложить свое уникальное видение и опыт, что позволяет нам решать задачи эффективно и рационально. Даже при наличии значительной разницы в уровне знаний предметной области или в технологических вопросах, более опытный партнер все равно чему-то учится у второго — ну, скажем, узнаёт о новых «горячих клавишах» либо встречается с новым инструментом или библиотекой. Для менее опытного партнера такая работа — замечательный способ «набрать скорость».
Парное программирование популярно у сторонников гибкой разработки, хотя и не только у них. Иногда противники парной работы интересуются: «А почему я должен платить двум программистам за выполнение работы одного?» Конечно, не должны. Но дело в том, что работа в паре повышает качество, улучшает понимание предметной области, технологий и приемов работы (скажем, неочевидных приемов работы с интегрированной средой разработки, IDE), а также уменьшает отрицательное влияние лотерейного риска (когда один из ваших специалистов-разработчиков выигрывает в лотерею и увольняется на следующий же день).
Какова долгосрочная выгода от того, что вы узнаете о новой «горячей клавише»? Какой мерой мы измерим улучшение качества продукта, достигнутое работой в паре? Какой мерой измерить пользу от того, что ваш партнер не дал вам зайти в тупик в решении сложной проблемы? Одно исследование свидетельствует о приросте эффективности и скорости на 40 %.[29] А как оценить уменьшение «лотерейного риска»? Большинство плюсов работы в паре трудно измерить.
Кто должен работать в паре и с кем? Если вы новичок в команде, то важно, чтобы вашим напарником оказался опытный специалист. Столь же важно, чтобы он обладал хорошими навыками общения и наставничества. Если у вас недостаточно знаний в предметной области, работайте в паре с тем, кто ее хорошо знает.
Если нет уверенности, экспериментируйте: сотрудничайте с коллегами. Создавайте пары для решения интересных сложных проблем. Посмотрите, что у вас получится. Попробуйте несколько раз.
Две ошибки могут гасить одна другую (и тогда их трудно исправлять)
Аллан Келли
Код никогда не лжет, но может быть внутренне противоречивым. Иногда противоречия вызывают недоумение: как это вообще может работать?
В своем интервью[30] Аллан Клампп (Allan Klumpp), ведущий разработчик программного обеспечения для лунного модуля Apollo, раскрыл тот факт, что ПО управления двигателями содержало дефект, из-за которого