Автор: Sergey Teplyakov

clip_image002

Не так давно, мне посчастливилось взять интервью у Бертрана Мейера, того самогоJ, автора самого фундаментального труда в области ООП и разработке ПО (это я все о том же талмуде 'Объектно-ориентированное конструирование программных систем'), у человека, который внес неоценимый вклад в развитие инженерных аспектов разработки ПО, в формализацию объектной методологии, а также процесс разработки в целом.

Возможность эта появилась у меня благодаря Учебному Центру Люксофт, который организует мастер класс Бертрана по теме 'Design by Contract' (на который, кстати, могут записаться все желающие за умеренную плату,)). И мы решили, что было бы неплохо пообщаться с Бертраном до этого и обсудить некоторые интересные моменты разработки ПО.

Перед интервью я подготовил список вопросов на 3 страницы (!), к сожалению, из-за ограничения по времени я так и не узнал, чья же версия принципа открыт-закрыт является более разумной Бертрана или 'дядюшки' Боба. Но несмотря на это, мы успели обсудить много интересного, начиная от контрактов, заканчивая идеями из новой книги Бертрана под названием 'Agile!: The Good, the Hype and the Ugly'.

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

Дизайн и проектирование по контракту

Здравствуйте, Бертран. Я очень рад, что у меня появилась возможность пообщаться с человеком, который внес неоценимый класс в методологию разработки ПО вообще, и в объектно-ориентированную разработку в частности.

Одним из наиболее важных ваших вкладов является идея контрактного программирования (Design by Contract), поэтому именно с нее хотелось бы начать наш диалог. Итак, в чем заключается важность проектирования по контракту?

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

Ну и нужно сказать, что контракты это не исключительно мое изобретение. Корни этих идей лежат в трудах таких ученых, как Тони Хоар, Эдсгер Дейкстра и других.

Идеи контрактного программирования возникли в середине 80-х годов, но лишь сейчас они начали появляться в mainstream языках программирования и платформах разработки. Как по вашем, в чем причина столь долгой адаптации этих идей? Почему они поддерживаются столь малым числом языков программирования?

Конечно можно сказать, что идеи контрактного программирования все же распространились и многие программисты о них знают и используют Но вы правы, пользуются ими далеко не все программисты. Причина такого положения очень проста: для нормального использования контраков нужно использовать язык с их полноценной поддержкой. Конечно, можно добавить техники DbC (Design by Contract) в любой язык, но они будут не столь удобными, как в случае использования языка с их полноценной поддержкой.

В таком случае остается вторая часть вопроса: почему тогда столь мало языков поддерживают контракты? Конечно же есть Eiffel, из новых языков можно отметить язык D, а для платформы .NET есть библиотека Code Contracts (хотя это поддержка на уровне платформы, а не на уровне языка). Но это все же единицы...

Предусловия и постусловия довольно легко добавить в любой язык с помощью макросов или сторонней библиотеки. Например, есть библиотека Nana для поддержки контрактов в С++. О ней довольно много говорили несколько лет назад, но я не знаю, используется ли она сейчас или нет.

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

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

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

Насколько большое комьюнити языка Eiffel и в каких областях он применяется?

Сообщество относительно небольшое и не идет ни в какое сравнение с сообществами Java или C#. Тем не менее это значительное сообщество, которое растет довольно быстро.

Что касается области использования, то Eiffel обычно используется в критических приложениях и в областях с очень сложной доменной логикой: это финансы, сетевое программирование, здравоохранение, военные приложения и приложения для авиционной отрасли.

А что по вашему может стать следующим большим прорывов в области проектирования приложений вообще и проектирования надежных и качественных приложений в частности?

Сейчас активно продолжаются работы в области верифицируемого ПО, что позволит гарантировать отсутствие ошибок. Это всегда было целью языка Eiffel и некоторых других языков. Эта технология медленно, но все таки развивается. Уже сейчас можно доказать корректность программ в очень специфических условиях для простых (обычно старых) языков программирования.

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

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

В последнее время наметилась тенденция в популяризации функциональных языков и функциональной парадигмы программирования. Что вы скажите, является ли объектная технология конкурентом функциональному программированию?

Нет, эти две парадигмы не являются конкурентами, они успешно могут дополнять друг друга. Тем не менее, тенденция к функциональному программированию является важной и интересной.

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

С другой стороны, если общая структура приложения построена на основе объектов, то очень даже полезно, если некоторые ее части будут написаны на функциональном языке, для обеспечения простоты и возможности доказательства корректности, о которых я говорил ранее.

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

Да, я кажется читал эту статью, которая затем вошла в качестве одной из глав в книгу Beautiful Architecture.

Вы знаете об этом? Я очень впечатлен.

(Смеюсь...) Да, и насколько я помню, это был ваш ответ на статью Саймона Пейтона Джонса, в которой автор старался показать, что ФП подход является более предпочтительным.

Да, совершенно верно.

ПРИМЕЧАНИЕ
Речь идет о статье Бертрана 'Software Architecture: Functional vs. Object-Oriented Design in Beautiful Architecture', опубликованной в книге 'Идеальная архитектура. Ведущие специалисты о красоте программных архитектур.'. Эта статья Мейера была ответом на статью Саймона 'Composing contracts: an adventure in financial engineering.'

Давайте все же немного вернемся к вопросу OOP vs FP. Какие именно преимущества у функционального подхода на 'низком уровне'?

В Eiffel существует очень важный принцип, под названием Command-Query Separation Principle, который можно рассматривать, в некотором роде, как сближение ОО и ФП миров. Я не считаю, что наличие состояния это однозначно плохо. Но очень важно, чтобы мы могли ясно различать операции, которые это состояние изменяют (т.е. командами), и операции, которые лишь возвращают информацию о состоянии, его не изменяя (т.е. запросами). В других языках эта разница отсутствует. Так, например, в С/С++ часто пишут функции, которые возвращают результат и изменяют состояние.

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

Означает ли это, что метод в таких языках как C++/C#/Java, который лишь вычисляет результат и не производит побочных эффектов, также отвечает принципу CQSP?

Да, конечно.

Т.е. отличительной чертой запроса является не отсутствие каких-либо вычислений, а именно отсутствие побочных эффектов?

Совершенно верно!

Процессы, принципы и методологии разработки

Я недавно начал читать вашу книгу 'Agile!: The Good, the Hype and the Ugly' и обратил внимание, на ваше прагматичное отношение ко многим 'модным' сегодня тенденциям. А как вы относитесь к принципам программирования вообще, и SOLID принципам в частности?

Очень важно выделять хорошие и полезные принципы, это характерно для всех инжерерных дисциплин. Но тут важно, чтобы эти идеи служили не только ради популяризации идей конкретного человека, а чтобы за ними стояли точные и убедительные аргументы (rationale). Именно это я старался делаться с принципами проектирования, которые сам продвигал. Я очень положительно отношусь к трудам, которые пробуют определить хорошие принципы и методологии.

Интересно, что в каждое время есть свои 'проповедники'. Так, в 70-х годах были очень сильные 'методологисты', такие как Дейкстра, Хоар, Парнас и другие. Это были люди, которые совмещали в себе 'евангелизм' и глубину понимания обсуждаемой проблемы, особенно в области разработки корректных и надежных программ. В течении последних 20 лет было довольно мало интересных работ в этой области.

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

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

Что вы думаете о юнит-тестировании?

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

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

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

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

Мы обсудили связь тестов со спецификацией, а как насчет влияния тестов на дизайн. Есть ли связь юнит-тестов и дизайна приложения?

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

Вы сказали, что важно думать о дизайне класса на более ранних этапах, а не во время тестирования. Но ведь есть же TDD (Test-Driven Development), в котором предполагается использовать тесты на этапе дизайна и обдумать обязанности класса через тесты, и лишь затем переходить к реализации. Что вы думаете на этот счет?

Мне кажется, что здесь снова есть удачная и неудачная мысли. Хорошая мысль в том, что появилось правило, воспетое ХР (eXtreme Programming), что для каждой строки кода должен быть соответствующий тест. Поэтому каждый раз, когда вы пишите строку кода, метод или модуль вы должны написать один или несколько тестов.

Плохая идея в том, что тестами пытаются заменить спецификацию и появились мысли, что требования (requirements) не нужны, а вместо них достаточно использовать сценарии использования (use cases). Это глупая мысль, поскольку сценарий использования по отношению к требованиям играют ту же роль, что и тест по отношению к спецификации: сценарий использования является лишь частным случаем, из которого невозможно вывести полноценные требования.

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

Все подходы 'гибких авторов' направлены против идеи 'big upfront something' и с этой мыслью я крайне не согласен. Да, они правильно критикуют проекты, в которых на сбор и изучение требований, на обсуждение архитектуры и дизайн, тратится очень много времени без написания единой строчки кода. Но не стоит впадать в другую крайность. Мысль, что можно начинать писать систему без каких-либо начальных усилий по сбору требований и совершенно без затрат на дизайн, также утопична.

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

Совершенно верно.

Команда в Eiffel Software является распределенной. Сейчас, в эпоху аутсорса это типичная картина. Есть ли секреты, как упростить процесс разработки в таких условиях, и как распределенная команда влияет на процесс разработки?

Никакого секрета нет, мы просто следуем ряду полезных принципов.

Во-первых, мы уделяем внимание коммуникациям. В нашей команде мы проводим 2 встречи в неделю по одному часу каждая, что сделать достаточно сложно, учитывая разные часовые пояса, в которых находятся ее члены, но мы с этим справляемся.

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

Вторая встреча проходит в четверг и тоже длится один час. Но для нее мы заранее готовим четкий план (agenda), который публикуем в Google Docs. В понедельник мы никогда не обсуждаем детали и если на встрече возникает технический вопрос, не решаемый за минуту, то мы переносим его на четверг.

Эта техника двух встреч, мне кажется, работает очень хорошо. У нас есть возможность для каждого члена команды пообщаться друг с другом физически через Skype, WebEx, Citrix или подобный инструмент, проверить состояние релиза, а также обсудить трудные технические вопросы.

Второй наш принцип это использование инспекций кода (code review). Я также написал статью на эту тему (прим., речь идет о статье 'Design and Code Reviews in the Age of the Internet'. Кстати, статья очень классная!). Эта практика очень хорошо работает, если подходить к ней с умом. Инспекция кода состоит из двух этапов: офлайнового и онлайнового. Вначале каждый инспектирует код самостоятельно, а затем мы проводим встречу, к которой тщательно готовимся. Мы письменно излагаем вопросы, которые собираемся обсуждать, и которые вызвали разногласия в команде. Мы стараемся использовать время встречи максимально эффективно, обсуждая интересные вещи и проблемы, по которым не было единого мнения.

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

О языках программирования

Давайте вернемся немного к языкам программирования. Я обратил внимание, что новая версия языка Eiffel выходит дважды в год и следующий релиз должен состояться очень скоро в этом мае. Я посмотрел на план этого релиза и увидел, что одно из нововведений звучит так: functional programming mechanisms and other language facilities. Интересно, о каких таких 'функциональных механизмах' идет речь?

Нужно сказать, что базовые механизмы функционального программирования агенты (прим. это локальные или анонимные методы в Eiffel) появились 10 или 12 лет назад. Данные изменения касаются прежде всего синтаксиса, чтобы писать в функциональном стиле стало проще, а синтаксис стал короче. Это значит, что на языке Eiffel можно будет писать программу или ее часть таким образом, что она будет очень похожей на программы на Haskell или ML.

Не все знают, что хотя Eiffel является объектно-ориентированным языком, на нем вполне можно писать программы в функциональном стиле. Это было возможным давно, но синтаксис был несколько тяжелым, так что мы тщательно продумали и добавили новые техники и выражения, которые позволят писать более короткие и изящные программы не жертвуя ОО-принципами вообще и принципа языка Eiffel в частности.

В последнее время во многих языках появилась поддержка асинхронного программирования на уровне языка (например, конструкции async/await в языке C#). Eiffel обладает поддержкой параллельного программирования в виде расширения SCOOP, а есть ли мысли расширения языка для поддержки асинхронного программирования?

Я очень положительно отношусь к новым возможностям async/await в C#, но мне кажется, что для их полноценного использования все же нужно понимать достаточно сложные детали. С первого взгляда кажется, что пользоваться этими конструкциями просто, но в некоторых случаях они могут оказаться такими же сложными, как и ручное управление многопоточностью и асинхронностью.

В SCOOP мы пробуем добавиться того, чтобы параллельное программирование было таким же простым, как и последовательное, однопоточное. По поводу же вашего вопроса: SCOOP является надмножеством и 'из коробки' поддерживает вещи, аналогичные async/await, причем делает это просто и надежно.

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

Да, именно так.

Насколько я помню из вашей книги 'Объектно-ориентированное конструирование', одним из главных лозунгов языка Eiffel является следующий: в языке должна быть одна и только одна возможность выполнить некоторую задачу. Именно поэтому до последнего времени был лишь один способ организации цикла в Eiffel. Не изменилась ли эта точка зрения со временем?

Во-первых, лозунг звучит так: в любом языке есть множество способов достижения цели, но должна быть лишь одна ХОРОШАЯ возможность сделать это.

Да, принцип все еще существует, хотя появились критики, что мы уже не следуем ему так строго, как раньше. Когда вы упомянули по поводу циклов, то видимо имели ввиду появление в языке Eiffel нового вида циклов across (прим. это конструкция аналогична циклу foreach языка C#). Очевидно, что если речь идет о простом переборе элементов списка или другой структуры данных, то across является наиболее подходящей формой цикла. Если же нужна более сложная форма цикла, то традиционный вариант всегда остается доступным.

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

Так, я очень строго борюсь против появления новых ключевых слов. Обычно довольно легко найти вескую причину для добавления нового ключевого слова, и если бы я не был 'врединой', то за последние 20 лет количество ключевых слов в Eiffel уже удвоилось бы. Это не только моя заслуга, эту точку зрения разделяют многие мои коллеги и мы вместе боремся с естественной тенденцией расширения языка и его 'фичеризма'. Тем не менее, Eiffel развивается и это уже не тот язык, каким он был 20 или даже 5 лет назад.

Спасибо Бертран. Нам пора заканчивать наш диалог и в конце я хотел бы задать вопрос общего характера. Что по-вашему является наиболее полезной чертой разработчика? Что нужно развивать в себе, чтобы стать успешным программистом или архитектором?

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

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

----------------------

У меня осталась масса вопросов, но к сожалению время Бертрана было ограниченным и мы так и не смогли поговорить более глубоко о гибких методологиях, современных принципах проектирования и многих других вопросах. Тем не менее, мне было очень интересно пообщаться с Бертраном и я надеюсь, что вам было приятно читать его интервью,)

UPDATE

Поступило предложение добавить пруф, хотя бы в виде скриншота нашего интервью:

image

Помогла статья? Оцените её!
0 из 5. Общее количество голосов - 0
 

You have no rights to post comments

Дмитрий Крикунов

Публикую статьи, обучающие курсы и новости по программированию: алгоритмам, языкам (С++, Java), параллельному программированию, паттернам и библиотекам (Qt, boost).