Боже, временами я просто ненавижу объектно-ориентированное программирование.
Наверное, я не один такой. Бессмертные слова Эдсгера Дейкстры гласят:
Обычно я не жалуюсь, но сейчас, думаю, самое время оглянуться назад и посмотреть, что же не так с ООП. В таком духе я и подготовил скромный список десяти вещей, которые я терпеть не могу в ООП.
Что же есть парадигма объектно-ориентированного программирования? Есть ли на это прямой ответ? Я слышал столько разных версий, что и сам теперь не знаю.
Если мы посмотрим на идеи Smalltalk’а, мы столкнемся с мантрой: “Все есть объект”. Кроме переменных. И пакетов. И примитивов. Числа и классы тоже не совсем объекты, и так далее. Очевидно, “все есть объект” не может быть основой парадигмы.
Что для ООП является фундаментальным? Питер Вегнер однажды предположил, что основой объектно-ориентированного программирования являются объекты, классы и наследование. Однако, каждый язык программирования поддерживает их по-разному, или даже не поддерживает по умолчанию некоторые из них. Ясно, что это тоже не парадигма ООП.
Другие заверяют, что ООП — это Инкапсуляция, Абстракция и Сокрытие данных. Правда, разные источники скажут вам, что это всего лишь разные слова для описания одинаковой идеи. А другие скажут, что это три фундаментально разные концепции.
С середины 80-ых об ООП гуляет несколько мифов. Один из них, Миф о повторном использовании, гласит, что ООП делает разработку продуктивней, потому позволяет наследовать и расширять текущий код вместо того, чтобы всякий раз писать его заново. Другой, Миф о проектировании, который подразумевает, что анализ, проектирование и реализация плавно следуют друг из друга, потому что все они — суть объекты. Конечно же, ни один из этих мифов не может быть парадигмой ООП.
Давайте взглянем на другие парадигмы, позволяющие решать программистские задачи определенным образом. Процедурное программирование часто описывается как программы = данные + алгоритмы. Логическое программирование гласит: программы = факты + правила. Функциональное программирование, по всей видимости, программы = функции + функции. Что наводит на мысль, что ООП означает программы = объекты + сообщения. Так ли это? Я думаю, что тут теряется главное. А главное в том, что ООП – это не парадигма, как например процедурное, логическое или функциональное программирование. Вместо этого ООП говорит: “для каждой отдельной задачи вы должны разработать свою собственную парадигму”. Другими словами, парадигма объектно-ориентированного проектирования такова: “Программирование — это моделирование”.
Еще я не люблю то, как всем нравится не любить чужие языки. Мы любим разделять мир на лагери: фигурные скобочки против квадратных против круглых.
Вот некоторые вещи, которые говорились о наших любимых ООЯП:
И, возможно, наиболее всеобъемлющий приговор:
Классы сводят меня с ума. Это может показаться странным, так что позвольте объяснить.
Кажется, что классы — это удобно. Наш мозг превосходно классифицирует информацию, которую мы получаем из окружающего мира. И было бы вполне естественно классифицировать все в объектно-ориентированных программах.
Однако в реальном мире есть только объекты. Классы существуют только в нашем сознании. Можете ли вы привести хоть один пример из реального мира, что класс — это реальная, физическая сущность? Нет, не думаю.
И вот в чем проблема. Вы когда-нибудь задумывались, почему программу на объектно-ориентированном языке понять настолько сложнее, чем на процедурном?
В процедурных программах процедуры вызывают другие процедуры. Процедурный код показывает… процедуры, вызывающие другие процедуры. Все хорошо и просто, так ведь?
В объектно-ориентированных программах объекты посылают сообщения другим объектам. Объектно-ориентированный код показывает… классы, наследующие другие классы. Ой. Кажется, что в ООП между исходным кодом и исполняемой программой нет никакой связи. Наши инструменты плохо помогают нам: IDE ведь показывают классы, а не объекты.
Я думаю, поэтому SmallTalk’еры так любят программировать в дебаггере: последний дает им почуствовать контроль над исполняемыми объектами и позволяет программировать их напрямую.
Вот, что я бы хотел сказать разработчикам инструментов: пожалуйста, дайте нам IDE, которая будет показывать объекты вместо классов!
Честно говоря, методы я тоже терпеть не могу.
Как мы все понимаем, методы в хороших объектно-ориентированных программах должны быть короткими и изящными. Множество маленьких методов удобнее для разработки, понимания, повторного использования и так далее. Так в чем же проблема?
Примем во внимание то, что на чтение объектно-ориентированного кода мы тратим больше времени, чем на его написание. Это и называется производительность: вместо того, чтобы часами писать большое количество кода, чтобы добавить новый функционал, мы пишем всего лишь несколько строчек, но при этом часами пытаемся понять, какие именно строчки нужно написать!
Одна из причин, почему мы тратим столько времени, в том, что нам приходится листать код туда-сюда… через множество маленьких методов.
Это также известно как синдром Затерянных в космосе и об этом говорится со времен создания ООП. Цитируя Адель Голдберг, “В SmallTalk все происходит где-то еще”.
Я уверен, что винить в этом стоит сегодняшние кодоориентированные IDE — учитывая, что объектно-ориентированный код не дает представлений об исполняемом приложении, IDE встает на нашем пути вместо то, чтобы помогать. Другая причина, по которой SmallTalk’еры так любят программировать в дебаггере — он помогает им хорошо видеть, какие объекты с какими взаимодействуют. Поэтому, я думаю, сегодня популярна разработка через тестирование (test-driven development, TDD), позволяющая увидеть взаимодействие объектов во время разработки.
И дело не в самом ООП — мы просто еще не поняли (после более чем 40 лет), как надо разрабатывать программы для него. Мы должны задаться вопросом: почему исходный код остается преобладающим отображением в IDE?
Я хочу IDE, которая позволит мне переключаться между кодом и исполняемым приложением. (Для получения представления об этой идее, загляните на сайт платформы для веб-разработки Seaside, позволяющей переключаться прямо с исполняемого приложения на редактируемый исходный код)
Ладно, признаю: я нетерпелив и ненавижу повторять слова дважды. Типы же заставляют меня это делать.
Кто-то сейчас наверняка подумал: “Но как же ты можешь писать на бестиповом языке. Ты никогда не будешь уверен, что твой код правилен.”
Конечно, “бестиповых” языков не существует — существуют статически и динамически типизированные. Статически типизированные мешают писать код в некоторых случаях. В принципе, ничего плохого в этом нет.
Проблема с самими типами, какими мы их знаем. Во-первых, они дают приводят к чувству ложной безопасности. То, что ваша Java программа компилируется, не означает, что в ней нет ошибок (и даже ошибок с типами).
Во-вторых и в-главных, типизация предполагает, что мир постоянен, а программы в нем состоят из неизменных частей, а это не так. Из-за этого некоторые полезные типы программ (особенно рефлексивные) становится сложнее писать.
И наконец, типизация плохо ладит с тем фактом, что существуют разные понятия типов. Не бывает одной всемогущей системы. Вспомните те страдания, которые мы испытывали с обобщениями в Java. Сейчас разрабатывается много полезных систем типов, но расширить Java, чтобы уместить их все, мы не можем. Гилад Браха предложил сделать типизацию не только необязательной, чтобы запускать программы даже в том случае, если типизация некорректна, но и подключаемой, такой, чтобы мы смогли подключать разные системы типов в разных частях наших программ. Мы должны подумать над этим предложением, а заодно и над тем, как адаптировать языки программирования и среды разработки к разнообразным системам типов.
Мы все не любим меняться, правда? А если так, то почему же жалуемся, если вещи не становятся лучше? Мы знаем, что полезные программы должны меняться, или со временем они ухудшаются.
(Между прочим, знаете, в чем разница между железом и софтом? Железо ухудшается, если вы не поддерживаете его в рабочем состоянии.)
Учитывая, что реальные программы должны меняться, справедливо полагать, что языки и IDE должны поддерживать это. Предлагаю вам, однако, назвать хоть один механизм в языках программирования, который поддерживает изменчивость. Те механизмы, которые работают с изменчивостью, скорее, ограничивают и контролируют ее.
Наш мир непостоянен, но мы хорошо себя в нем чувствуем. Контекст — вот то, что позволяет нам справляться с переменами. Мы подстраиваем наше поведение, наши ожидания под контекст, в котором находимся, но наши программы почему-то ломаются, если контекст меняется.
Я хочу увидеть эту идею в объектно-ориентированных языках и IDE. Как исходный код, так и исполняемое приложение должны иметь возможность подстраиваться под изменяющийся контекст. Уверен, что многие шаблоны проектирования и идеи (такие как посетитель и внедрение зависимости) — всего лишь костыли, которые исчезнут после реализации механизма контекстов в ООЯП.
Паттерны. Не могу с ними, не могу без них.
Каждый шаблон проектирования делает ваш проект запутаннее.
Я все сказал.
Похоже, некоторые из моих студентов работают по Agile-методологии в стиле Чака Норриса:
“Чак Норрис не занимается итеративной разработкой. Все получается с первого раза, каждый раз.”
“Чак Норрис не пишет документацию. Он смотрит в упор на код до тех пор, пока тот не скажет все, что надо знать.”
Бертран Мейер рассказывает о том, что его удивляло, почему схематические языки программирования всегда были такими популярными, пока однажды его не осенило: “Пузыри не ломаются!”. (Другое высказывание, принадлежащее Мейеру: “All you need is code”)
Похоже, что с тем, что обычно понимается под разработкой через моделирование, тоже что-то не так: не код должен генерироваться из модели — модель должна быть кодом.
Между прочим, FORTRAN продавался как язык высокого уровня, из которого генерировался исходный код. А теперь язык высокого уровня для нас и есть исходный код.
Надеюсь, однажды, когда мы повзрослеем, модель будет считаться считаться исходным кодом.
И наконец, коронная фраза, которую я ненавижу: “Объектов недостаточно. Нужно еще...”. Все эти годы нам нужны были фреймворки, компоненты, аспекты, сервисы (которые, похоже, любопытным образом вернули нас к процедурному программированию!)
Если объектов никогда не было достаточно, почему же они исправно служили нам все эти годы?
25 лет назад мы не ожидали, что “новый” феномен ООП проживет столь долго.
Мы думали, что ОО-конфереции типа ECOOP, OOPSLA просуществуют лет 5, а затем затеряются в мейнстриме.
Но и сейчас слишком рано игнорировать ООП как часть мейнстрима. А тот факт, что научные и промышленные исследования в области объектно-ориентированного программирования еще продолжаются, подсказывает, что происходит что-то важное, чего мы еще не понимаем полностью.
ООП позволяет упрощать сложные вещи через моделирование, но мы все еще не овладели этим, возможно, потому, что плохо различаем существенные и несущественные сложности.
Чтобы двигаться дальше, мы должны сосредоточиться на изменчивости и на том, как ООП может содействовать изменчивости. Спустя столько лет мы все еще находимся в самом начале, пытаясь понять, что же ООП может предложить нам.
Спасибо за прочтение!
Немного об авторе
Oscar Nierstrasz — профессор Computer Science в Бернском университете, основатель Software Composition Group, которая исследует механизмы, позволяющие компьютерным системам быть гибче и лучше приспосабливаться к изменяющимся условиям. (отсюда понятно стремление автора к внедрению механизмов изменчивости в ЯП)
Активный участник исследовательского ООП-сообщества, участник и председатель таких конференций, как ECOOP, ESEC и MODELS. (Данная статья — как раз речь Oscar'а на конференции ECOOP 2010). Также главный редактор The Journal of Object Technology.
Соавтор более 200 публикаций и 2 книг: Object-Oriented Reengineering Patterns и Pharo by Example. (обе книги, по-видимому, не переведены на русский, зато бесплатны и доступны для скачивания по ссылкам)
Наверное, я не один такой. Бессмертные слова Эдсгера Дейкстры гласят:
«Объектно-ориентрованное программирование — это исключительно плохая идея, которую могли придумать только в Калифорнии.”
Обычно я не жалуюсь, но сейчас, думаю, самое время оглянуться назад и посмотреть, что же не так с ООП. В таком духе я и подготовил скромный список десяти вещей, которые я терпеть не могу в ООП.
1. Парадигма
Что же есть парадигма объектно-ориентированного программирования? Есть ли на это прямой ответ? Я слышал столько разных версий, что и сам теперь не знаю.
Если мы посмотрим на идеи Smalltalk’а, мы столкнемся с мантрой: “Все есть объект”. Кроме переменных. И пакетов. И примитивов. Числа и классы тоже не совсем объекты, и так далее. Очевидно, “все есть объект” не может быть основой парадигмы.
Что для ООП является фундаментальным? Питер Вегнер однажды предположил, что основой объектно-ориентированного программирования являются объекты, классы и наследование. Однако, каждый язык программирования поддерживает их по-разному, или даже не поддерживает по умолчанию некоторые из них. Ясно, что это тоже не парадигма ООП.
Другие заверяют, что ООП — это Инкапсуляция, Абстракция и Сокрытие данных. Правда, разные источники скажут вам, что это всего лишь разные слова для описания одинаковой идеи. А другие скажут, что это три фундаментально разные концепции.
С середины 80-ых об ООП гуляет несколько мифов. Один из них, Миф о повторном использовании, гласит, что ООП делает разработку продуктивней, потому позволяет наследовать и расширять текущий код вместо того, чтобы всякий раз писать его заново. Другой, Миф о проектировании, который подразумевает, что анализ, проектирование и реализация плавно следуют друг из друга, потому что все они — суть объекты. Конечно же, ни один из этих мифов не может быть парадигмой ООП.
Давайте взглянем на другие парадигмы, позволяющие решать программистские задачи определенным образом. Процедурное программирование часто описывается как программы = данные + алгоритмы. Логическое программирование гласит: программы = факты + правила. Функциональное программирование, по всей видимости, программы = функции + функции. Что наводит на мысль, что ООП означает программы = объекты + сообщения. Так ли это? Я думаю, что тут теряется главное. А главное в том, что ООП – это не парадигма, как например процедурное, логическое или функциональное программирование. Вместо этого ООП говорит: “для каждой отдельной задачи вы должны разработать свою собственную парадигму”. Другими словами, парадигма объектно-ориентированного проектирования такова: “Программирование — это моделирование”.
2. Объектно-ориентированные языки программирования
Еще я не люблю то, как всем нравится не любить чужие языки. Мы любим разделять мир на лагери: фигурные скобочки против квадратных против круглых.
Вот некоторые вещи, которые говорились о наших любимых ООЯП:
“Си позволяет легко выстрелить себе в ногу; с C++ это сделать сложнее, но, когда вы это делаете, вы отстреливаете себе ногу целиком.”Это сказал Бьерн Страуструп, так что все хорошо.
“Я придумал термин ‘объектно-ориентированный’, и вот что я вам скажу: я не имел в виду C++.” — Алан Кей.
“В C++ всего 2 вещи получились не так: начальный замысел и реализация.” — Бертран Мейер
“Внутри С++ сидит более компактный и понятный язык, отчаянно пытающийся выбраться наружу.” — Бьерн Страуструп
“C++ — это история, повторяющаяся как трагедия. Java — это история, повторяющаяся как фарс.” — Скотт МакКей
“Java, лучший аргумент за SmallTalk после C++.” — Фрэнк Винклер
“Если бы у Java был настоящий сборщик мусора, большинство программ удаляли бы себя во время исполнения.” — Роберт Сьюэл
И, возможно, наиболее всеобъемлющий приговор:
“Есть всего 2 типа языков: те, на которые все жалуются и те, которыми никто не пользуется.” — Бьерн Страуструп
3. Классы
Классы сводят меня с ума. Это может показаться странным, так что позвольте объяснить.
Кажется, что классы — это удобно. Наш мозг превосходно классифицирует информацию, которую мы получаем из окружающего мира. И было бы вполне естественно классифицировать все в объектно-ориентированных программах.
Однако в реальном мире есть только объекты. Классы существуют только в нашем сознании. Можете ли вы привести хоть один пример из реального мира, что класс — это реальная, физическая сущность? Нет, не думаю.
И вот в чем проблема. Вы когда-нибудь задумывались, почему программу на объектно-ориентированном языке понять настолько сложнее, чем на процедурном?
В процедурных программах процедуры вызывают другие процедуры. Процедурный код показывает… процедуры, вызывающие другие процедуры. Все хорошо и просто, так ведь?
В объектно-ориентированных программах объекты посылают сообщения другим объектам. Объектно-ориентированный код показывает… классы, наследующие другие классы. Ой. Кажется, что в ООП между исходным кодом и исполняемой программой нет никакой связи. Наши инструменты плохо помогают нам: IDE ведь показывают классы, а не объекты.
Я думаю, поэтому SmallTalk’еры так любят программировать в дебаггере: последний дает им почуствовать контроль над исполняемыми объектами и позволяет программировать их напрямую.
Вот, что я бы хотел сказать разработчикам инструментов: пожалуйста, дайте нам IDE, которая будет показывать объекты вместо классов!
4. Методы
Честно говоря, методы я тоже терпеть не могу.
Как мы все понимаем, методы в хороших объектно-ориентированных программах должны быть короткими и изящными. Множество маленьких методов удобнее для разработки, понимания, повторного использования и так далее. Так в чем же проблема?
Примем во внимание то, что на чтение объектно-ориентированного кода мы тратим больше времени, чем на его написание. Это и называется производительность: вместо того, чтобы часами писать большое количество кода, чтобы добавить новый функционал, мы пишем всего лишь несколько строчек, но при этом часами пытаемся понять, какие именно строчки нужно написать!
Одна из причин, почему мы тратим столько времени, в том, что нам приходится листать код туда-сюда… через множество маленьких методов.
Это также известно как синдром Затерянных в космосе и об этом говорится со времен создания ООП. Цитируя Адель Голдберг, “В SmallTalk все происходит где-то еще”.
Я уверен, что винить в этом стоит сегодняшние кодоориентированные IDE — учитывая, что объектно-ориентированный код не дает представлений об исполняемом приложении, IDE встает на нашем пути вместо то, чтобы помогать. Другая причина, по которой SmallTalk’еры так любят программировать в дебаггере — он помогает им хорошо видеть, какие объекты с какими взаимодействуют. Поэтому, я думаю, сегодня популярна разработка через тестирование (test-driven development, TDD), позволяющая увидеть взаимодействие объектов во время разработки.
И дело не в самом ООП — мы просто еще не поняли (после более чем 40 лет), как надо разрабатывать программы для него. Мы должны задаться вопросом: почему исходный код остается преобладающим отображением в IDE?
Я хочу IDE, которая позволит мне переключаться между кодом и исполняемым приложением. (Для получения представления об этой идее, загляните на сайт платформы для веб-разработки Seaside, позволяющей переключаться прямо с исполняемого приложения на редактируемый исходный код)
5. Типы
Ладно, признаю: я нетерпелив и ненавижу повторять слова дважды. Типы же заставляют меня это делать.
Кто-то сейчас наверняка подумал: “Но как же ты можешь писать на бестиповом языке. Ты никогда не будешь уверен, что твой код правилен.”
Конечно, “бестиповых” языков не существует — существуют статически и динамически типизированные. Статически типизированные мешают писать код в некоторых случаях. В принципе, ничего плохого в этом нет.
Проблема с самими типами, какими мы их знаем. Во-первых, они дают приводят к чувству ложной безопасности. То, что ваша Java программа компилируется, не означает, что в ней нет ошибок (и даже ошибок с типами).
Во-вторых и в-главных, типизация предполагает, что мир постоянен, а программы в нем состоят из неизменных частей, а это не так. Из-за этого некоторые полезные типы программ (особенно рефлексивные) становится сложнее писать.
И наконец, типизация плохо ладит с тем фактом, что существуют разные понятия типов. Не бывает одной всемогущей системы. Вспомните те страдания, которые мы испытывали с обобщениями в Java. Сейчас разрабатывается много полезных систем типов, но расширить Java, чтобы уместить их все, мы не можем. Гилад Браха предложил сделать типизацию не только необязательной, чтобы запускать программы даже в том случае, если типизация некорректна, но и подключаемой, такой, чтобы мы смогли подключать разные системы типов в разных частях наших программ. Мы должны подумать над этим предложением, а заодно и над тем, как адаптировать языки программирования и среды разработки к разнообразным системам типов.
6. Изменчивость
“Перемены неизбежны — в отличие от мелочи из торгового автомата.” — Роберт Галагер
Мы все не любим меняться, правда? А если так, то почему же жалуемся, если вещи не становятся лучше? Мы знаем, что полезные программы должны меняться, или со временем они ухудшаются.
(Между прочим, знаете, в чем разница между железом и софтом? Железо ухудшается, если вы не поддерживаете его в рабочем состоянии.)
Учитывая, что реальные программы должны меняться, справедливо полагать, что языки и IDE должны поддерживать это. Предлагаю вам, однако, назвать хоть один механизм в языках программирования, который поддерживает изменчивость. Те механизмы, которые работают с изменчивостью, скорее, ограничивают и контролируют ее.
Наш мир непостоянен, но мы хорошо себя в нем чувствуем. Контекст — вот то, что позволяет нам справляться с переменами. Мы подстраиваем наше поведение, наши ожидания под контекст, в котором находимся, но наши программы почему-то ломаются, если контекст меняется.
Я хочу увидеть эту идею в объектно-ориентированных языках и IDE. Как исходный код, так и исполняемое приложение должны иметь возможность подстраиваться под изменяющийся контекст. Уверен, что многие шаблоны проектирования и идеи (такие как посетитель и внедрение зависимости) — всего лишь костыли, которые исчезнут после реализации механизма контекстов в ООЯП.
7. Шаблоны проектирования
Паттерны. Не могу с ними, не могу без них.
Каждый шаблон проектирования делает ваш проект запутаннее.
Я все сказал.
8. Методологии
“Все методологии основаны на страхе.” — Кент Бэк
Похоже, некоторые из моих студентов работают по Agile-методологии в стиле Чака Норриса:
“Чак Норрис не занимается итеративной разработкой. Все получается с первого раза, каждый раз.”
“Чак Норрис не пишет документацию. Он смотрит в упор на код до тех пор, пока тот не скажет все, что надо знать.”
9. UML
Бертран Мейер рассказывает о том, что его удивляло, почему схематические языки программирования всегда были такими популярными, пока однажды его не осенило: “Пузыри не ломаются!”. (Другое высказывание, принадлежащее Мейеру: “All you need is code”)
Похоже, что с тем, что обычно понимается под разработкой через моделирование, тоже что-то не так: не код должен генерироваться из модели — модель должна быть кодом.
Между прочим, FORTRAN продавался как язык высокого уровня, из которого генерировался исходный код. А теперь язык высокого уровня для нас и есть исходный код.
Надеюсь, однажды, когда мы повзрослеем, модель будет считаться считаться исходным кодом.
10. Очередная новая вещь
И наконец, коронная фраза, которую я ненавижу: “Объектов недостаточно. Нужно еще...”. Все эти годы нам нужны были фреймворки, компоненты, аспекты, сервисы (которые, похоже, любопытным образом вернули нас к процедурному программированию!)
Если объектов никогда не было достаточно, почему же они исправно служили нам все эти годы?
Вывод?
25 лет назад мы не ожидали, что “новый” феномен ООП проживет столь долго.
Мы думали, что ОО-конфереции типа ECOOP, OOPSLA просуществуют лет 5, а затем затеряются в мейнстриме.
Но и сейчас слишком рано игнорировать ООП как часть мейнстрима. А тот факт, что научные и промышленные исследования в области объектно-ориентированного программирования еще продолжаются, подсказывает, что происходит что-то важное, чего мы еще не понимаем полностью.
ООП позволяет упрощать сложные вещи через моделирование, но мы все еще не овладели этим, возможно, потому, что плохо различаем существенные и несущественные сложности.
Чтобы двигаться дальше, мы должны сосредоточиться на изменчивости и на том, как ООП может содействовать изменчивости. Спустя столько лет мы все еще находимся в самом начале, пытаясь понять, что же ООП может предложить нам.
Спасибо за прочтение!
Немного об авторе
![](https://habrastorage.org/storage2/9aa/286/f02/9aa286f02ba08d3cd49c00df3ed24206.jpg)
Активный участник исследовательского ООП-сообщества, участник и председатель таких конференций, как ECOOP, ESEC и MODELS. (Данная статья — как раз речь Oscar'а на конференции ECOOP 2010). Также главный редактор The Journal of Object Technology.
Соавтор более 200 публикаций и 2 книг: Object-Oriented Reengineering Patterns и Pharo by Example. (обе книги, по-видимому, не переведены на русский, зато бесплатны и доступны для скачивания по ссылкам)