avva: (moose)
[personal profile] avva
У факультета компьютерных наук в Carnegie-Mellon University очень высокая репутация, он известен как один из самых лучших в Америке - именно этот факультет, а не весь университет. Так вот, в нем, оказывется, решили не преподавать больше объектно-ориентированное программирование в вводных курсах. Те студенты, что захотят с ним познакомиться, смогут это сделать на факультативном предмете для второго курса:

Object-oriented programming is eliminated entirely from the introductory curriculum, because it is both anti-modular and anti-parallel by its very nature, and hence unsuitable for a modern CS curriculum. A proposed new course on object-oriented design methodology will be offered at the sophomore level for those students who wish to study this topic.

Этой новости уже два года - не знаю, как там у них с этим все вышло. В комментариях к этой блог-записи есть умеренно интересный спор о том, действительно ли ООП "anti-modular and anti-parallel by its very nature", в котором присутствует много академического жаргона.

Я не знаю, что думать конкретно об идее "не преподавать ООП на первом курсе CS". Профессор CMU утверждает, что одной из причин их решения было то, что они встретились с выпускниками CMU, работающими сейчас в Facebook, и те им сказали, что больше всего в университете им помог курс функционального программирования, так что они решили давать побольше этого. Проблемы с этим аргументом очевидны: FP необязательно должно целиком вытеснять OOP, а главное, эту информацию они получили от людей, которые будучи студентами как раз начинали с OOP-языка. Но с другой стороны, есть резон и в том, что факультет CS должен давать студентам теоретические начала, а также учить их думать и понимать, а не только обучать писать программы, и на престижном факультете, куда идут абитуриенты высокого уровня, может и лучше это делать с помощью императивных и функциональных языков, а OOP считать чем-то, что можно потом подучить.

Поскольку я вижу обе стороны этого спора, и не могу между ними решить (недостаточно данных), я напишу вместо этого о применении OOP в индустрии, которое хоть и повсеместно - это глупо отрицать, но довольно значительно изменилось за последние 20 лет. Мне кажется, что в этом - в том, как применять OOP - мы коллективно многому научились, хоть этот опыт плохо согласуется с "философией" OOP, скажем так.

Я думаю, что развитие OOP-языков и то, как они применяются на практике, особенно в больших проектах, показало за последние 20 лет следующую тенденцию. Главная польза OOP была и остается в том, как эта практика помогает модуляризации исходного кода. Под модуляризацией я понимаю в широком смысле все, что помогает программисту, с одной стороны, держать связанные друг с другом вещи вместе (как на экране, так и в уме), и с другой стороны, иметь дело с менее тесно связанными вещами раздельно. При этом "помогает" означает не только "делает возможным", но также - и даже более важно! - "делает обычным, очевидным, самым простым выбором".

Это может показаться на первый взгляд тривиальным утверждением, но не думаю, что оно совсем тривиально. Когда говорят о достоинствах OOP, упоминают всякие разные вещи. Например:
- моделирование проблемы или решения в виде иерархии объектов и их методов
- code reuse: использование уже написанного кода, который надо лишь немного изменить (или добавить в него), с помощью наследования
- энкапсуляция, скрытие внутренней имплементации от внешних пользователей

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

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

1. Начнем с самого основного: соединения кода и данных в одном классе. Это оказалось невероятно полезной идеей, которая, возможно, отвечает за львиную долю всей пользы от OOP вообще. При этом согласно академическим или пуристическим понятиям это даже необязательно OOP вообще, но это неважно. Представьте себе C++ без виртуальных функций, наследования и полиморфизма, просто с классами, в которых есть данные и методы. Неважно, что специалист по теории языков программирования скажет, что это не OOP вообще, а тривиальный синтаксический сахар (а что-то типа CLOS в Лиспе, где нет четкой привязки методов к классам, наоборот, самый что ни на есть OOP). Даже этот тривиальный синтаксический сахар сам по себе уже дает огромную пользу в смысле модуляризации. Оказывается, огромное количество решений, которые приходится писать на практике, естественным образом выглядят так: небольшое количество связанных друг с другом данных, и функции для работы именно с этими данными. Понятие класса помогает держать эти связанные вещи вместе, а не связанные вещи - отдельно, в другом классе (и другом файле обычно).

Конечно, для этого не обязательно иметь OOP или понятие класса. Мне приходилось иметь дело с большими проектами, написанными на C; в них обычно оказывалось, что во многих исходных файлах определена одна-две небольших структуры, хранящие связанную друг с другом информацию, и рядом - функции для работы с этой структурой. Имена функций и структур подчеркивают их связь; часто складывается конвенция, что функции получают указатель на структуру первым аргументом. Все это работает, но чтобы это поддерживать, нужно стараться. Если не стараться (или если поручить дело слабенькому программисту), со временем функции для работы с этой структурой растекутся в другие файлы, имена будут не очень ясные, итд. Классы дают эту модуляризацию практически бесплатно и поддерживают ее тоже почти бесплатно. В одном этом уже - огромное их преимущество. Это также главная причина, по которой C++ победил C.

2. Наследование по интерфейсу помогает модуляризации. Определяется контракт, и он разделяет в пространстве (исходного кода) и уме (программиста) имплементацию и использование этого контракта. В больших проектах это незаменимо. Должен быть какой-то способ решить: я делаю это, а ты делаешь это, и мы потом вот по этой линии стыкуемся. Эта линия будет контрактом, как ее ни назови; но моделировать ее в виде интерфейса или абстрактного класса очень удобно (в динамических OOP-языках происходит то же самое, просто контракт задается неявно или в документации, а не в виде типа). Кстати, в небольших проектах это тоже необходимо, даже если есть только один программист: "я" и "ты" просто означает "я сегодня" и "я завтра". Должна быть возможность думать только о части системы.

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

3. Наследование кода (implementation inheritance) мешает модуляризации, и поэтому почти всегда оказывается плохой идеей. Я не могу окинуть взглядом одну штуку (класс) и понять ее поведение, мне нужно учитывать другие штуки (классы), которые написаны обычно в другом месте, в другом файле. Одного этого достаточно, чтобы навредить модуляризации. Этот вред обычно оказывается значительнее, чем польза от code reuse. Говоря в терминах Джавы, implements намного полезнее extends. Множественное наследование (multiple inheritance) в этом смысле еще хуже, и вреда от него - в реальном мире и с реальными программистами, а не в идеальном мире, населенном гуру - намного больше, чем пользы. Решение Джавы отказаться от множественного наследования кода вообще в этом смысле закономерно. Триумф STL, в которой практически нет наследования кода, над другими потенциальными стандартными библиотеками для C++ - закономерен.

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

Date: 2013-03-13 01:08 am (UTC)
From: [identity profile] cryinstone.livejournal.com
"Object-oriented programming... is both anti-modular (!) and anti-parallel (?!) by its very nature"
---
WTF?

Date: 2013-03-13 04:41 am (UTC)
From: [identity profile] migmit.livejournal.com
За параллельность не скажу, но насчёт anti-modular — чистая правда.

Date: 2013-03-13 05:31 am (UTC)
From: [identity profile] cryinstone.livejournal.com
И как именно ООП мешает модулярности?

Date: 2013-03-13 07:22 am (UTC)
From: [identity profile] migmit.livejournal.com
Это большой и сложный вопрос, и я не уверен, что ответ, который я дам сейчас, будет достаточно верным.

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

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

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

Представьте, что методы std::vector стали виртуальными, и кто-то в них нашаманил - нет, не изменяя библиотеку, а просто отнаследовавшись. Когда вы в своём коде получите извне этот якобы вектор (успешно прикидывающийся обычным вектором) и начнёте делать с ним какие-то базовые вещи - у вас пойдут безумные баги, которые вам будет крайне сложно отследить. Вы получите все "преимущества" отсутствующей модульности.

Date: 2013-03-13 09:01 am (UTC)
From: [identity profile] cryinstone.livejournal.com
Если кто-то отнаследует от вектора, и сделает это скверно - этот самый измененный вектор будет находится в другом модуле-библиотеке, что я непременно смогу заметить - откуда он подгружается :)

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

В общем, хотелось бы увидеть настоящих проблем, а не высосанных из пальца.

Date: 2013-03-13 09:33 am (UTC)
From: [identity profile] migmit.livejournal.com
> Если кто-то отнаследует от вектора, и сделает это скверно - этот самый измененный вектор будет находится в другом модуле-библиотеке, что я непременно смогу заметить - откуда он подгружается

Вот вам приходит вектор:
void doSomething(std::vector &list) {
  //ваш код хиар
}


Прокомментируйте, пожалуйста, как вы заметите, что "царь не настоящий"?

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

Да, но к ООП это не имеет никакого отношения. Класс становится банальной записью, ну плюс ещё неймспейсом.
Edited Date: 2013-03-13 09:33 am (UTC)

Date: 2013-03-13 04:52 am (UTC)
From: [identity profile] golos-dobra.livejournal.com
Возможно профессора CMU знают что-то за пределами ваших личных представлений о мире?

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

Date: 2013-03-13 08:26 am (UTC)
From: [identity profile] dumalkin.livejournal.com
"Вы когда-нибудь запускали больше хотя бы тысячи процессов одновременно?"
Какая связь ?
У меня одна из систем поддерживает сотни тысяч параллельно обрабатываемых объектов на 96 процессорах.
Объектов, с функциями, данными, интерфейсами и т.д.
Которые кстати можно подгружать (включая новые типы, новые реализации интерфейсов) не меняя и не перекомпилируя ничего.
Мне ООП ни с модулярностью ни с паралелльностью не мешает.

Date: 2013-03-13 01:36 pm (UTC)
From: [identity profile] golos-dobra.livejournal.com
Связь прямая, вы делаете классический MIMD, в парадигме восьмидесятых, как и положено в индустрии с отставанием на полвека. Сейчас второе десятилетие 21 века на дворе и парадигма снова меняется. Детей учить надо на будущее.

Date: 2013-03-13 01:42 pm (UTC)
From: [identity profile] dumalkin.livejournal.com
Что я, простите, делаю классический ?
И какая у нас парадигма на дворе ?
Мне просто интересно, скоро выученные дети ко мне на производство придут, надо быть готовым.
МЛ и Хаскелл в универе - это прикольно и полезно, я в свое время АПЛ учил - развлекался, но почему под этим соусом надо выбрасывать то, что можно применить на практике ?

Date: 2013-03-13 02:01 pm (UTC)
From: [identity profile] golos-dobra.livejournal.com
Речь не о "выбрасывать", а о подготовке нового поколения к вещам, за которые будут платить деньги завтра.

Когда объемы данных в петабайт будут считаться "средним", стандартным по индустрии уровнем.

Date: 2013-03-13 02:12 pm (UTC)
From: [identity profile] dumalkin.livejournal.com
"Когда объемы данных в петабайт будут считаться "средним", стандартным по индустрии уровнем."
Все еще непонятно какая связь.
Сначала ООП мещало параллелизации работы.
Теперь оно мешает обработке большого количества информации.

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

2.Сейчас данные в несколько гигабайт съедаются даже на телефоне, но большинство приложений не работают с такими размерами напрямую. Либо их нет вообще, либо они манипулируются через БД. Что изменится когда телефон сможет в себе держать 100 петабайт?

3. Даже если сейчас мне надо будет написать нечто, напрямую обрабатывающее петабайтные объемы данных - при чем здесь ООП, чем она мне помешает ?

Date: 2013-03-13 02:32 pm (UTC)
From: [identity profile] golos-dobra.livejournal.com
ООП мешает не параллелизации, ООП мешает эффективной параллелизации, где надо отслеживать синхронизацию каждого байта по миллиону процессоров.

Обрабатывать терабайты как сейчас в фоновом режиме и в режиме реального времени несколько разные принципиально вещи.

Раньше, еще даже в девяностые, упор делался на "умный", потом на легче поддерживаемый корпоративной инфраструктурой код. Теперь Кнута с прочей сортировкой можно забыть и выкинуть, потому что совсем другие вещи стали актуальны.

Date: 2013-03-13 02:42 pm (UTC)
From: [identity profile] dumalkin.livejournal.com
"ООП мешает не параллелизации, ООП мешает эффективной параллелизации, где надо отслеживать синхронизацию каждого байта по миллиону процессоров."
Почему ?
У нас сейчас объектная система бежит на многих компьютерах. По вашей логике это супер не эффективно, поскольку надо синхронизировать миллионы байт на сотнях процессорах.
Если бы это действительно происходило - это было бы неэффективно. Но этого не происходит, потому что никто не сказал что ООП требует / предполагает симметричность нахождения данных / доступности ВСЕХ объектов на ВСЕХ нодах.

OOP ортогональна концепции SOA, или в общем случае - software as service.
Можно написать SOA и ООП, можно ООП и не СОА и т.д.

С ООП можно писать stateless / statefull, centralized / distributed, synchronous / asynchronous, parallel / single threaded и т.д. приложения. Все они могут при этом быть (или не быть) ООП.

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

Date: 2013-03-13 03:22 pm (UTC)
From: [identity profile] golos-dobra.livejournal.com
Вы же сами сказали - 78 процессоров, а для такого уровня параллелизации можно чем угодно пользоваться.

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

Date: 2013-03-13 03:36 pm (UTC)
From: [identity profile] dumalkin.livejournal.com
"Вы же сами сказали - 78 процессоров"
96 на одном ноде. А их много.
"Для более серьезных и актуальных вещей нужен абсолютно точный контроль потока исполнения"

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

Нашу ООП систему можно было бы написать как набор сервисов СОА, с функциональной реализацией каждого сервиса. Она все равно осталась бы асинхронной и параллельной.
А можно было, оставив ООП, написать все синхронно и stateless, с тем самым контролем потока исполнения. Получилась бы совершенно другая система, с другой производительностью, с другими слабыми / сильными сторонами, но она все еще была бы ООП.

ООП - это способ организации кода. Его основное влияние - не на этапе работы системы, а на этапе написания / поддержки. Он помогает (или мешает) человеку понять что происходит в коде, помогает отразить в коде то, что происходит в problem domain. Причем обычно в рамках ООП способов отражения (моделирования) - куча, разной степени правильности, и разные способы и результаты по разному подходят под разные архитектуры / технологии.

Date: 2013-03-13 04:03 pm (UTC)
From: [identity profile] golos-dobra.livejournal.com
Некоторые считают что им Экселя хватает на все случаи жизни, особенно чтобы что-то понять и отразить.

Поразительные трюки выделывают в нем.

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

Date: 2013-03-13 04:04 pm (UTC)
From: [identity profile] dumalkin.livejournal.com
"Суть дела остается - ООП есть корпоративные кандалы"
Ок, тогда я добровольный собственный раб :)

December 2025

S M T W T F S
  123 4 56
78 9 10 11 1213
1415 1617181920
21 22 23 24 2526 27
28293031   

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Dec. 29th, 2025 10:09 pm
Powered by Dreamwidth Studios