Интервью с Питером Соммерладом

Замечание: Данный материал является переводом с английского. Перевод выполнен мной, а я не профессиональный переводчик. Замечания и предложения по улучшению перевода принимаются. Как вариант, вы можете читать оригинал на английском.


Если у вас есть класс, для которого, как вам кажется, надо реализовать конструктор копирования или оператор присваивания, что-то вы делаете явно не так. // Питер Соммерлад


Сегодня в гостях у моего блога Питер Соммерлад. Мне довелось недавно побывать на его выступлении на ежегодной конференции ACCU в Оксфорде. Питер давно занимается C++, и более того, принимает участие в работе над стандартом C++. Будучи профессором университета HSR (Рапперсвиль, Швейцария), вместе со своими студентами и аспирантами он работает над разнообразными библиотеками и средствам разработки для С++ и ряда других языков под Eclipse.

У нас будет возможность задать Питеру вопросы, которые не являются частью его выступлений или публикаций.

Здравствуйте Питер, спасибо за интервью. Я не буду оригинальным с первым вопросом, но пропустить его не могу. Как так случилось, что вы начали заниматься компьютерами и стали программистом? Расскажите нам свою историю.

У меня были очень хорошие оценки по математике в школе, и в выпускном году был дополнительный курс, где надо было работать с программируемым калькулятором HP 33, в основанного на стеке операндов. В отличие от своих друзей, у которых уже были калькуляторы Casio и TI-59, или даже домашний компьютер Commodore V(I)C 20, я ничего не знал о программировании. Однако, благодаря своему учителю, мне удалось портировать игру “lunar lander”, о которой я прочитал в журнале, с основанного на регистрах калькулятора TI-59 на школьный HP 33. В итоге у меня получилась баллистическая “стрелялка”, где надо было с определенного расстояния поразить цель, загороженную препятствиями. Эдакие Angry Birds восьмидесятых для карманного калькулятора с семисегментным жидкокристаллическим экраном в десять разрядов. Требовалось определенное воображение, чтобы понять эту игру: вводится два числа, угол и скорость, и программа сообщает, попали ли вы в препятствие или насколько было отклонение от цели. Такая вот была моя первая программа.

Тот курс программирования для HP 33 и также идея старшей сестры, знавшая людей, изучавших информатику и при этом зарабатывающих неплохие деньги, будучи студентами, подтолкнули меня записаться на курс математики и начать изучать информатику во Франкфурте. Перед началом учебы я получил свой первый домашний, “настоящий” компьютер, Sinclair ZX Spectrum, c невообразимым тогда объемом памяти в 48Кб и кассетным магнитофоном. Тут уже можно было программировать на Бейсике, что означало, как говорил наш учитель математики, “компьютер мог сам распределять память под переменные, и не надо было помнить, что ты поместил в регистры R1 и R2”. Одной из моих первых программ для ZX Spectrum была отрисовка фигуры в форме сердца (да, “заливка” цветом подобной области была непростой задачей). Я показал это подруге, но, честно говоря, не уверен, что она оценила это, так как я начал уделять компьютеру больше времени, чем ей. Мы женаты уже более двадцати лет, но иногда я напоминаю ей эту историю. :-)

Затем пришло время университета, и в первом семестре мне пришлось научиться программировать на ассемблере для Univac 1100. После лекции про косвенную адресацию, мы с другом написали программку из двух команд, которая в бесконечном цикле загружала свои собственные команды. Когда мы ее запустили, через 2 секунды получили “watchdog error”, что на мейнфрейме означает, что все остальные программы были остановлены на две секунды. После трех таких запусков, в зал “влетел” оператор из серверной и начал выяснять, кто работает под нашим с товарищем логином. Мы медленно подняли руки. Он препроводил нас к себе в кабинет, и после расспросов как и что мы делали, таки отпустил нас с миром, взяв обещание не рассказывать другим студентам про этот “прием”. Во втором семестре начался Паскаль и “настоящие” программы. Указатели в Паскале были реальной задачей, так как они появились без какого-либо объяснения, для чего они нужны, или примера, типа связанного списка. Только после этого я смог понять их. Через нескольких семестров я начал подрабатывать в небольшой фирме, созданной старшими сокурсниками. Там у меня была возможность реально применять полученные знания и набираться опыта на реальным задачах и языках (dBaseII, Dataflex, UCSD Pascal и Microsoft Pascal). Например, на Microsoft Pascal под DOS я написал библиотеку для создания баз данных, основанной на B*-дереве.

Очень большое влияние, я полагаю, на меня оказала возможность во время диплома работать с исходными текстами UNIX, и особенно утилит из нее, таких как make, awk и т.д. Я написал фронтенд для Modula-2 на С с использованием lex и yacc. Чтобы иметь возможность работать из дома, мне пришлось портировать все необходимые утилиты на шестнадцатибитный компьютер с DOS. На было разобраться, как все работает изнутри, и работы Денниса Ритчи и его коллег были отличным примером. Возможно, оттуда берет начало моя любовь к простому и элегантному коду.

Можете вспомнить свою “Первую Книгу” про компьютеры и программирование? И какая книга оказала на вас набольшее влияние?

Одной из первых книг, что я помню, были “Pascal User Manual and Report”, Kathleen Jensen и Niklaus Wirth, и “Algorithms and Data Structures = Programs”, Niklaus Wirth. Кстати, в последней было много ошибок, если слепо набивать примеры. Например, в той редакции, что была у меня, сортировка слиянием была нестабильной, вопреки сути этого алгоритма.

В целом, было много книг, повлиявших на мои взгляды в программировании, и непросто выбрать что-то конкретное. Могу рассказать про книгу, где я являюсь соавтором: “Pattern-oriented Software Architecture: A System of Patterns”. В процессе ее написания, я очень многое узнал о программном обеспечении в целом, его архитектуру и работу в команде. Уже после публикации, выступая на конференциях и рассказывая о книге, я познакомился со множеством интересных людей, которые без сомнения повлияли мои взгляды. С некоторыми мы стали друзьями.

Мне очень понравилось ваше высказывание на ACCU, что перед тем, как использоваться “крутые” возможности C++, мне нужно сначала получить у вас лицензию. Мне эта идея показалась очень полезной при обучении C++ или, например, при составлении стандарта кодирования в компании. Но как именно вы разделяете язык на “разрешенные и рекомендованные” возможности и на лицензированные?

Сложный вопрос. Я имел в виду C++11 и множество приемов, которые люди использовали раньше, и которые более не нужны, если вы не разрабатываете библиотеки. Но лучше привести пример.

В книге Страуструпа “The C++ Programming Language” в качестве примера создания класса рассказывается про класс Vector, и как там происходит управление памятью через указатели. Проблема в том, что правильная и безопасная с точки зрения исключений реализация такого класса является очень сложной даже для экспертов C++. Рядовой же читатель, решив сделать подобный класс или, например, класс для строчек, скорее всего сделает это неверно. Более того, делать это совершенно не нужно.

Это похоже на ситуацию с книгой Вирта про алгоритмы и структуры данных. “Все” студенты, кому приходилось ее изучать, хотят реализовать свой связный список просто потому, что он им “все равно нужен”. Может на C это и имело смысл, но в библиотеках современных языков (не только в С++) есть множество хороших и проверенных структур данных. Я был мог “порассуждать” про качество библиотек в Java, но я оставлю это моему коллеге, преподающему алгоритмы и структуры данных в Java в нашем университете.

Сейчас, после того как мы поняли, чего делать не надо, я бы рекомендовал просто использовать стандартную библиотеку C++, особенно алгоритмы, так как с помощью них даже циклы, которым нас учили, можно не использовать. В С++11 нам более не требуется управлять ресурсами вручную. Можно просто использовать std::vector, std::string, std::shared_ptr/std::unique_ptr и т.д., и компилятор сам создаст правильные конструкторы и деструкторы. Если у вас есть класс, для которого, как вам кажется, надо реализовать конструктор копирования или оператор присваивания, что-то вы делаете явно не так. Кстати, с boost это также почти всегда верно и для C++03.

Так что, до тех пор, пока вы глубоко не изучили приемы создания библиотек, просто используйте стандартные и не изобретайте своих. И даже после этого, избегайте “ручного” управления ресурсами. Чем больше я погружался в проблему создания надежным переносимых стандартных библиотек, тем больше проникался уважением к тем, кто имеет навыки и терпение для этого. Это реально сложно. А большинство прикладных задач, решаемых на С++, не требуют подобных знаний, если вы используете стандартную библиотеку. Хотя, ничто не мешает вам стать разработчиком библиотек.

Над чем именно в C++ 2011 вы работали?

Мне не очень много удалось сделать для C++ 2011, так как я весьма поздно подключился к проекту. Однако, мне удалось получить финансирование от нашей школы (спасибо директору, профессору Херману Меттлеру) для проведения собрания комитета стандартизации C++ в Рапперсвиле в Августе 2010-го. Несколько предложений в разделе про std::async были переписаны мной при поддержке многих других членов комитета. В процессе написания книги “Simple C++” я часто ссылаюсь на стандарт и периодически нахожу незначительные ошибки, которые будут исправлены в следующей редакции.

Есть ли что-то в С++ 2011, что вам реально не нравится?

Что значит “не нравится”? Я могу принять стандарт как есть, так как я знаю, чего стоило его создать. Все недостатки, которые могли бы меня касаться, будут исправлены в следующей редакции, и я работаю над этим. Порой вы должны идти на компромисс с комитетом.

Если б я, будучи вовлеченным в процесс раньше, предложил что-то, и мое предложение было бы отклонено другими членам, я был расстроился. Размышляя на эту тему, я бы сказал, мне не нравится, что const разрешено ставить слева, тогда как логика подсказывает, что это должно быть справа. Оба варианта синтаксически одинаковы. Я спрашивал, что будет, если я сделаю подобное предложение. Скорее всего оно будет отклонено “старшими товарищами”.

Давайте поговорим про TDD. Я знаю, что вы разработали библиотеку для unit-тестирования CUTE. Наверное, у вас были серьезные на то причины. Что такого интересного есть в вашей библиотеке?

Для начала, библиотека состоит только из заголовочных файлов, и поэтому не надо думать о линковке. Это особенно важно для моих студентов. Я написал статью про CUTE в журнале “ACCU Overload #75” (слегка измененный вариант статьи можно скачать с http://wiki.hsr.ch/PeterSommerlad/wiki.cgi?CuTe). Главная причина в том, что меня сильно не устраивала библиотека CppUnit и также другая unit-тест библиотека, разработанная мной ранее в девяностых. Вдохновленный Кевлином Хенни я попробовал использовать стандартную библиотеку и минимизировать использование макросов, даже если это означало, что тесты должны быть вручную “зарегистрированы”, а не автоматически, используя статическую инициализацию (например, GoogleTest использует для этого макросы). Я хотел избежать статической инициализации, так как я уже имел с ней проблемы в прошлом при использовании разделяемых библиотек и их зависимостей. Проблема регистрации тестов решается нашим плагином CUTE для Eclipse CDT (http://cute-test.com). Плагин сгенерирует необходимый код для регистрации теста и также проверит на случай, если вы забыли сделать это вручную. Конечно удобно, когда есть возможность просматривать различия, регистрируемые через ASSERT_EQUAL(), но это требует возможность печати значений в поток std::ostream&. Это является проблемой для разработчиков встраиваемых систем, если они хотят запускать тесты на “железе”, так как из-за ограничений по размеру кода не удается использовать iostream. Однако, у меня в планах выпустить в этом году версию CUTE, где можно будет конфигурировать использование iostream.

Двое моих студентов реализовали поддержку TDD в Eclipse CDT, а один из аспирантов довел до состояния законченного продукта. Например, наш модуль TDD может автоматически создавать объявления переменных, которые используются в коде, но еще не объявлены. Аналогично для новых необъявленных классов, перечислений (enums), свободных функций или членов классов. Это очень удобно вне зависимости от стиля разработки: TDD или сверху вниз. Скоро в плагине CUTE будет добавлена еще одна возможность: Mockator. Mockator генерирует код и фрагменты конфигурации Eclipse CDT для применения техники “dependency injection” в уже существующем коде на С или С++ с использованием так называемых швов. Идея швов описывается в отличной книге Майкла Физерса “Эффективная работа с унаследованным кодом” (Michael Feathers, “Working Effectively with Legacy Code”). По этой методике Mockator может создавать шаблоны тестов и mock’и для функций и типов. Про Mockator можно прочитать в выпуске 108 журнала “ACCU Overload”.

IDE против Vi. Я знаю, вы активно используете и дорабатываете Eclipse, чтобы можно было удобно работать с тестами и проводить рефакторинг. Как вы думаете, возможно ли все еще, используя Vi и make, эффективно разрабатывать сложные системы? или среды разработки вроде Eclipse уже просто необходимы из-за сложности современных программных проектов?

Я использую vi с 1985 года, и все еще создаю в нем небольшие файлы, разнообразные одноразовые скрипты. Мне нравится, что vi есть везде.

Однако, для серьезного программирования хорошая автоматизированные среда (IDE) просто необходима. Это как ехать на сотни миль на мощной машине или на детском трехколесном велосипеде. Конечно, есть люди, ездящие на детском велике очень быстро, но на машине просто в разы удобнее. Наверное, аналогия с машиной не самая удачная, и вертолет лучше бы тут подошел, так как хорошая среда разработки (например, Eclipse CDT) позволяет быстро перемещаться по коду, поэтому можно очень легко разбираться, даже если его очень много. Чтобы перейди от объявления к определению достаточно навести мышку или кликнуть, при этом среда помнит, где вы были, и можно быстро вернуться. Любой программист на Java, использующий Eclipse, наверное захочет использовать другой язык, только если для него будут похожие инструменты. Мы стараемся сделать Eclipse CDT таким же удобным для C++, как и для Java. Это непросто, и мы рады любому спонсорству.

Как вы думаете, является ли спортивное программирование и соревнования по нему обязательными для любого, изучающего информатику?

В нашей университетской программе мы всегда даем студентам очень много лабораторных работ в курсах программирования. Как минимум 3-4 больших проекта являются частью диплома бакалавра. Выполнение лаб нетривиальным образом реально помогает студентам. Например, я часто даю упражнения на С++, которые надо выполнять без циклов или рекурсии, а только используя алгоритмы из STL. Решение с циклом может быть очевидным, но найти красивый алгоритм несравнимо полезнее.

Увы, некоторые соревнования по программированию ориентированы исключительно на скорость, а не на качество кода в далекой перспективе. Поэтому я всегда, а не только на лабах, но и на экзаменах прошу студентов писать unit-тесты.

Мы учимся на примерах. Можете посоветовать начинающим, где можно посмотреть примеры хорошего современного кода?

Для C++11 такие примеры найти пока непросто, так как компиляторы появились только недавно. В дополнение к этому, много примеров на C++11 с новыми возможностями языка, например, семантика перемещения, которые не объясняют где и как стоит применять эти возможности. Надо помнить, что многие реализации стандартной библиотеки гораздо сложнее, чем код, с которым вам хотелось бы работать, так как приходится иметь дело с макросами, обеспечивать обратную совместимость и разбираться с недостатками компиляторов. Решение подобных проблем не для новичка, и к тому же дает неверное представление о том, как надо писать код. Если честно, даже в CUTE, так как я пытался поддержать C++03/boost, std::tr1 и C++11, есть несколько “нехороших” мест.

Как вы думаете, разработка программного обеспечение – это ремесло или искусство? Считаете ли вы, что “правильная” программа обязана иметь красивые исходники? Или все что нужно, это чтобы требования были удовлетворены, тесты пройдены, стандарт кодирования соблюден, а дальше – трава не расти?

Программное обеспечение одновременно искусство и ремесло. Тут однозначно требуется теория и практика в ее совершенствовании. В реальности, невозможно отделить искусство от ремесла.

Я верю в красоту кода. Хотя это относительно, так как то, что хорошо для новичка, часто лишено элегантности кода, написанного профессионалом. Знание доступных инструментов просто необходимо, чтобы освоить их. Например, только знание std::vector без алгоритмов из STL позволит написать работающий, но не элегантный код, даже если это оценивается только метрикой сложности McCabe’а (кстати, это еще один плагин для C++ CDT, разработанные нашим студентом).

Мы были первыми после Билла Опдайка, кто попытался реализовать рефакторинг для C++. Для этого мы переделали большие куски внутренностей CDT и реализовали генерацию кода на основе AST (abstract syntax tree). Так что только запуска тестов не достаточно – код еще должен быть “очищен”. Я много рецензирую код и вижу, как часто можно сделать лучше. Я правда не уверен, что талант можно автоматизировать, так как это смесь вкуса и опыта. Тут требуется не только видеть саму проблему, но и пути ее решения. С другой стороны, если посмотреть на примеры “дурно пахнущего” кода по Мартину Фаулеру, часто можно встретить противоречивые примеры.

Некоторые стандарты кодирование часто промоутируют то, что я называю “плохим” стилем, и привносят ненужные усложнения. Например, Гугл предлагает так называемые выходные параметры передавать через указатели, даже в С++. Это может делаться для того, чтобы на вызывающей стороне приходилось писать foo(&var), явно передавая переменную по указателю, тем самым улучшая читабельность. Однако, этот прием, необходимый в С, где не было ссылок, снова привносит проблемы, уже решенные введением ссылок в С++. В такой функции надо проверять указатель на nullptr (в терминах C++11), и никогда нельзя быть уверенным, что этот указатель правильный в случае ошибки в вызывающем коде, и надо игнорировать проблему, просто аварийно завершая программу, если был передан nullptr. Если решать проблему в стиле С++, то надо использовать ссылки, void foo(type & var);, и среда разработки покажет это, когда наведете мышкой на параметр, без явного использования амперсанда. Стандарт кодирования должен помогать использовать возможности языка и среды разработки для создания более компактного и простого кода, а не рассчитывать на старые редакторы, или просто на незнание их возможностей.

Стандарт кодирования или правила хороши только когда они выполняются. Средства, какие как (PC-)lint могут в этом помочь. Часто подобные классические утилиты работают из командной строки и применяются на стадии сборки. Более современные их варианты встроены в среду и делают не только анализ, но и предлагают на месте исправить проблему, например, FingBugs для Java или FXCop для C#. Их настройка, чтобы исключить ложные срабатывания, всегда непростая задача. Чтобы упростить применение lint мы создали Linticator, плагин для Eclipse, который автоматически настраивает lint и визуализируют результаты его работы в IDE (http://linticator.com).

Можете назвать три самых больших “нет” для программистов в целом?

Быть слишком самоуверенным, вместо написания (unit) тестов и регулярного их запуска. Любая разработка без автоматизированного тестирования является, на мой взгляд, непрофессиональным подходом.

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

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

Какие языки программирования вы бы порекомендовали для начинающих?

Вариантов очень много. Я не хотел бы рекомендовать что-то конкретное, так как в каждом есть свои недостатки. Важнее изучить несколько языков, причем которые основаны на разных парадигмах: функциональные, объектно-ориентированные, динамические или со статическом типизацией, компилируемые, интерпретируемые, использующие виртуальную машину или компилируемые в машинный код. Говорят, что на любом языке можно написать программу на Фортране. Этого можно и не делать со множеством языков, но важно знать и применять языковые идиомы.

В наше время очень много обучающих материалов доступно в онлайне. Так можно даже диплом получить. Как вы думаете, очное обучение все еще имеет смысл?

Да. Работа со студентами очень важна. И не только для их пользы, но и для моей, как преподавателя. Это позволяет улучшать программу обучения. Непосредственное взаимодействие очень важно для обеих сторон. Заочное обучение не дает этого, но является удобным, для тех, кто по каким-то причинам не может присутствовать лично. Но мне кажется, что сделать качественное заочное обучение весьма дорого.

В боевых искусствах есть концепция школ, стилей, направлений. И каждый старается доказать свое превосходство. Есть ли что-то подобное в мире программирования?

Я слышал, люди используют аналогии из боевых искусств, например, упражнения по программированию (coding dojos). Я не владею боевыми искусствами, и мой единственный спорт, за исключением кардио-тренировок – это горные лыжи. Это не командный спорт, но позволяющий “поймать поток” и забыть обо всем. С работой тоже самое. Но программное обеспечение – это командный спорт, и отработка стандартных движений, вроде “ката”, помогает развивать подсознательный вкус к хорошему коду и стилю, но только если он у вас уже есть, или кто-то помогает вам его развить. По мой взгляд, есть проблема – многие преподаватели, обучающие новичков, не имеют практического опыта программирования (это относится и ко мне, но я по крайней мере стараюсь регулярно работать с кодом).

Если б вам надо было нанять хорошего программиста на С++, как бы вы это делали? C++ – очень сложный язык, так что, как бы вы оценивали знания по нему?

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

В наши дни также важно уметь адаптироваться к изменениям и быть подкованным с точки зрения архитектуры. Кандидат должен понимать шаблоны программирования и знать, что существует больше, чем 23 GOF (Gang of Four Design Patterns) шаблона.

Ну и напоследок, завершающий вопрос: как вы думаете, в области программного обеспечения, мы выбираем профессию, или профессия выбирает нас?

Я начал с выбора курса в университете. Однако, чем хорошо программное обеспечение, что мы можем создавать свои собственные инструменты. Это как бы если пекарь мог бы испечь себе новую печь или миксер для теста. Так что поучившись немного, я выбрал профессию. Я занимаюсь этим уже 30 лет, из них 25 лет преподаю (я начал это, когда был еще студентом), пишу об этом книги и статьи уже около 18 лет и все еще обожаю свою профессию. Я хочу сделать этот мир лучше для программистов и надеюсь это сделать, убрав плохие программы. Знаю, это, конечно, невозможно, но это дает мне понять, куда двигаться.

Спасибо Питер. С нетерпением ждем ваши новые проекты и выступления.

// Май 16, 2012

// Питер Соммерлад, Александр Дёмин


Disclaimer

Комментарии