В этой теме я хочу коснуться именно принципиальных, концептуальных проблем ОО парадигмы, а не применимости ее в конкретных проектах.
Итак.
1). ООшные дизайн паттерны плохо учитывают многонитевость и параллельное программирование.
В этом разделе программирования очень важную роль играют атомарные операции, в общем почти транзакции (свойства A и I точно есть). То есть — некая совокупность изменений связанных структур данных должна делаться обязательно до конца, и ее промежуточные состояния не видимы другим субъектам исполнения.
В свете этих атомарных операций большую роль играет то, какие именно куски большой структуры есть объект атомарной операции. Грубо говоря, "какой лок что защищает".
Еще одна сложность в том, что зачастую по ходу этой "транзакции" нужно делать какие-то медленные, занимающие неизвестное время операции. Естественно, это нужно делать вне "транзакции" (иначе потеря скалабильности и шансы на дедлок), т.е. разбивать "транзакцию" на две, вводить промежуточное состояние всей большой структуры, во время которого и делается медленная операция.
Классическое ОО, которое означает — "куча мелких кусочков, каждый есть чуть-чуть данных и чуть-чуть кода" не дает средств как-то вразумительно это описать. То есть — структурный, Сишный подход в коде, богатом многонитевостью, ничем не хуже. "ООшность" не дает выигрыша, например, в случае, когда большая структура (над которой оперируют транзакции) есть родитель с переменным количеством дочерних объектов, да еще и со ссылками наружу.
2). В ОО очень плохо решен вопрос операций сразу на большим количеством похожих элементов данных.
ООшное решение для такого — обернуть каждый элемент данных в объект, создать контейнер объектов, и потом итерировать контейнер.
Кроме итераций, ОО практически ничего не предлагает.
Пример: сохранение в файл большого количества мелких элементов данных. Если такая задача стоит часто и требования к ней по скорости высоки, то имеет смысл хранение данных в памяти проектировать именно "от сохранения в файл". Т.е. — много страниц, каждая из которых сохраняется в файл одним вызовом write(). В странице находятся элементы данных, которые идут там "встык", как в Сишном массиве. Удаление из середины и реюз удаленного места делается, например, списком пустых мест.
Это не-ООшный паттерн. Совсем. Операции оперируют над отдельными объектами, в то время как хранятся сразу агрегаты объектов, и есть агрегатные операции — сохранение в файл.
ОО для решения задачи может только предложить проитерировать контейнер и сериализовать каждый объект отдельно, с чудовищными накладными расходами.
Или взять задачу поиска, отбора элементов данных по какому-то признаку. ООшный стиль будет иметь много больший оверхед, чем вот такой "страничный".
В этом мне видится причина, почему "не пошли" ООБД. Связывать каждый элемент данных с кодом — расточительно. Это требует, например, на неких массовых операциях — инстанцирования каждого объекта. Медленно. SQLный SELECT быстрее.
Общее у этих двух недостатков: ОО предполагает кучу мелких сущностей, каждая из которых состоит из кода и из элемента данных. Это не всегда удобно. Иногда бывает удобно иметь сложные структуры именно одних лишь данных, а код к ним прилеплять уже по мере необходимости с различных боков. Т.е. классический процедурный, не ОО подход.
Здравствуйте, Maxim S. Shatskih, Вы писали:
> 1). ООшные дизайн паттерны плохо учитывают многонитевость и параллельное программирование. > ... > Классическое ОО, которое означает — "куча мелких кусочков, каждый есть чуть-чуть данных и чуть-чуть кода" не дает средств как-то вразумительно это описать.
Модель активных объектов в стиле Active Oberon постулирует:
Объект — это чуть-чуть данных и чуть-чуть кода для согласованных атомарных действий над этими данными.
И есть активные объекты — обладающие собственным потоком управления.
Здравствуйте, Maxim S. Shatskih, Вы писали:
MSS>1). ООшные дизайн паттерны плохо учитывают многонитевость и параллельное программирование.
MSS>В этом разделе программирования очень важную роль играют атомарные операции, в общем почти транзакции (свойства A и I точно есть).
+1
В принципе, неплохо бы иметь полноценные транзакции для операций над объектами. Это было бы полезно в ряде задач, во всяком случае — прикладных.
MSS>2). В ОО очень плохо решен вопрос операций сразу на большим количеством похожих элементов данных.
+1
может быть, еще:
3) Объект не может менять свой тип.
Есть конечно паттерн State, но это просто костыль.
Здравствуйте, absolute, Вы писали:
A>Модель активных объектов в стиле Active Oberon постулирует: A>Объект — это чуть-чуть данных и чуть-чуть кода для согласованных атомарных действий над этими данными. A>И есть активные объекты — обладающие собственным потоком управления.
это в чистом виде синтаксический сахар, который ничего не решает
Здравствуйте, absolute, Вы писали:
A>Кому синтаксический сахар, а кому и более высокий уровень абстракции.
A>Надеюсь, не нужно объяснять разницу между этими понятиями?
Не нужно.
А что нужно — объяснить, по сравнению с чем более высокий.
Здравствуйте, absolute, Вы писали:
A>Здравствуйте, Maxim S. Shatskih, Вы писали:
>> 1). ООшные дизайн паттерны плохо учитывают многонитевость и параллельное программирование. >> ... >> Классическое ОО, которое означает — "куча мелких кусочков, каждый есть чуть-чуть данных и чуть-чуть кода" не дает средств как-то вразумительно это описать.
A>Модель активных объектов в стиле Active Oberon постулирует: A>Объект — это чуть-чуть данных и чуть-чуть кода для согласованных атомарных действий над этими данными. A>И есть активные объекты — обладающие собственным потоком управления.
Да, мне тоже кажется, что ОО никак не противоречит параллельности. Если на концептуальном уровне каждый процес (или нить) — это объект, то переходим от параллельности процессов к взаимодействию объектов.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, Дарней, Вы писали:
A>>По сравнению с процессами, потоками и семафорами.
Д>Не вижу особой разницы. Все делается точно так же, только называется по другому
Попробуем провести такую параллель.
Существует очень хорошая, полезная и мощная концепция под названием "вызов подпрограммы". Чтобы произвести вызов подпрограммы, нужно положить на стек аргументы, адрес возврата, осуществить переход, в конце выполнения подпрограммы снять адрес возврата и перейти по нему, оставив на стеке результаты, которые затем будут использованы вызывающим кодом.
Последовательность нехитрая, но несколько утомительная для постоянного ручного кодирования. К счастью, программисты давным-давно могут уже не задумываться об этом. Операция "вызов подпрограммы" давно автоматизирована как на уровне языков программирования, так и на аппаратном уровне предоставлением машинных команд вызова и возврата.
Так вот. Параллельное программирование с использованием потоков и семафоров можно сравнить с ручным использованием стеков и адресов возврата для повседневного вызова процедур. Использование же активных объектов позволяет работать с параллелизмом на гораздо более высоком семантическом уровне. Прорыв сравним здесь с написанием вызова подпрограммы одной строчкой. Ничего хитрого там действительно нет. Например, оператор AWAIT языка Active Oberon, будучи разложен на элементарные действия, по сложности примерно равен вызову подпрограммы. Но в тексте программы он будет представлен одной строчкой, ясно показывающей наши намерения.
Здравствуйте, LaptevVV, Вы писали:
LVV>Да, мне тоже кажется, что ОО никак не противоречит параллельности. Если на концептуальном уровне каждый процес (или нить) — это объект, то переходим от параллельности процессов к взаимодействию объектов.
Отлично!
Именно так!
Средства межпроцессного взаимодействия, имеющие первостепенное значение в операционных системах "с процессами", превращаются в межобъектное взаимодействие. А межобъектное взаимодействие у нас одно — посылка сообщения. Или, на практике, процедурный вызов метода объекта.
Более того, все средства IPC можно реализовать на самом языке программирования Active Oberon. Ещё один бонус — все они будут подчиняться сильной типизации, в отличие от традиционно бестиповых IPC (например pipe — "поток неструктурированных байт", и т. п.).
Maxim S. Shatskih wrote: > Итак. > 1). ООшные дизайн паттерны плохо учитывают многонитевость и параллельное > программирование.
Ничуть. Все основные способы параллельности либо ортогональны ОО, либо
прекрасно выражаются с помощью ОО.
В частности:
1. Блокировки — абсолютно ортогональны ОО.
2. Атомарные структуры — прекрасно сосуществуют с ОО.
3. Параллельность с помощью посылки асинхронных сообщений — так вообще
как будто для ОО делалась.
> В этом разделе программирования очень важную роль играют атомарные > операции, в общем почти транзакции (свойства A и I точно есть). То есть > — некая совокупность изменений связанных структур данных должна делаться > обязательно до конца, и ее промежуточные состояния не видимы другим > субъектам исполнения.
См. JBoss4 — там делаются транзакции на (почти) произвольных графах
объектов с помощью инструментации байт-кода. В любом случае, эта
проблема к ОО отношения не имеет.
> Классическое ОО, которое означает — "куча мелких кусочков, каждый есть > чуть-чуть данных и чуть-чуть кода" не дает средств как-то вразумительно > это описать. То есть — структурный, Сишный подход в коде, богатом > многонитевостью, ничем не хуже. "ООшность" не дает выигрыша, например, в > случае, когда большая структура (над которой оперируют транзакции) есть > родитель с переменным количеством дочерних объектов, да еще и со > ссылками наружу.
ОО как раз позволяет неплохо выделить working set и следить за
контекстом выполнения. В чистом С это, естественно, тоже делается, но
уже достаточно искусственно.
Совершенно естественный код. И совершенно непотокобезопасный.
Теперь перепишем на С++:
std::string str="A tokinezed, string! Here!";
//Неплохо бы инкапсюлировать состояние токенизации.
//Конечно же, для этого лучше взять объект.
Tokenizer tok(str," , !");
for(std::string cur=tok.begin();!cur.empty();cur=token.next())
{
//Сразу возникает вопрос - а с какими потоками
//разделяется std::cout? Ну ладно, считаем вывод thread-safe
std::cout<<cur;
}
(Я сейчас про Erlang говорить не буду)
> 2). В ОО очень плохо решен вопрос операций сразу на большим количеством > похожих элементов данных. > ООшное решение для такого — обернуть каждый элемент данных в объект, > создать контейнер объектов, и потом итерировать контейнер.
Во-первых, смотрим на OpenMP — это я про то, что итерация замечательно
парллелится.
Во-вторых, смотрим на "map-reduce" — тоже замечательно дружит с ОО.
В-третьих, пока процессоры еще не научились делать что-либо, отличное от
последовательного перебора (ну за исключением векторных вычислений, но
это слишком узкий класс).
> Пример: сохранение в файл большого количества мелких элементов данных. > Если такая задача стоит часто и требования к ней по скорости высоки, то > имеет смысл хранение данных в памяти проектировать именно "от сохранения > в файл". Т.е. — много страниц, каждая из которых сохраняется в файл > одним вызовом write(). В странице находятся элементы данных, которые > идут там "встык", как в Сишном массиве. Удаление из середины и реюз > удаленного места делается, например, списком пустых мест.
Welcome to C++ — отображем файл на память, и используем аллокатор,
создающий объекты прямо в файле (см. Boost.Shmem). Можно даже контейнеры
в дисковую память отображать — я таким макаром mini-ООСУБД написал за час.
> Или взять задачу поиска, отбора элементов данных по какому-то признаку. > ООшный стиль будет иметь много больший оверхед, чем вот такой "страничный".
Ничуть. Берем Boost.RTL (Relational Template Library) и делаем,
например, так:
// allocate the expression to query for people joined
// between 12/5/2003 and 12/25/2003, and sort them by name
expr_type expr = key_index<mpl::vector<name, id> >(
range_selection(
key_index<mpl::vector<joined, id> >(t),
lower_bound(row<mpl::vector<joined> >(date(2003, Dec, 5))),
lower_bound(row<mpl::vector<joined> >(date(2003, Dec, 25)))
)
);
transaction tr;
// modify the table through the transaction
tr.insert(t, employee_tuple(11, "K", date(2003, Dec, 7)));
tr.insert(t, employee_tuple(12, "CC", date(2003, Dec, 17)));
tr.remove(t, *t.lower_bound(row<mpl::vector<id> >(5)));
expression_registry exprs;
exprs.add(expr);
// commit the transaction. Tell it to update the expression.
// two indexes get incrementally updated rather than rebuilt.
tr.commit(exprs);
// reuse the query
print(expr);
(кстати, это еще и к вопросу о транзакциях)
> Общее у этих двух недостатков: ОО предполагает кучу мелких сущностей, > каждая из которых состоит из кода и из элемента данных. Это не всегда > удобно. Иногда бывает удобно иметь сложные структуры именно одних лишь > данных, а код к ним прилеплять уже по мере необходимости с различных > боков. Т.е. классический процедурный, не ОО подход.
В ОО-подходе аггрегатная сущность может служить адаптером для мелких
сущностей. Так что ничего не противоречит.
Здравствуйте, absolute, Вы писали:
A>Кому синтаксический сахар, а кому и более высокий уровень абстракции.
A>Надеюсь, не нужно объяснять разницу между этими понятиями?
Синтаксический сахар для того и делается чтобы поднять уровень абстракции. Так что понятия зависимые но не сопостовимые.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Честно говоря, надоели уже до чёртиков все эти разговоры про недостатки ООП. Я предлагаю прекратить подобные инсинуации и начать проще относиться к жизни в целом и к ООП в частности. Нужно всего-то лишь перестать думать об ООП как о панацее и/или серебряной пуле и всё сразу станет на свои места.
Лично я уже давно отношусь к ООП просто как к набору удобных, хорошо зарекомендовавших себя дизайн паттернов, которые экономят мне кучу времени. Как-то после первого года работы на C++, мой шеф заставил меня писать обратно на чистом C, якобы в целях какой-то там совместимости. Помню, ломка у меня тогда началась с первых же строк кода. И что забавно после завершения работы я обнаружил практически у всех моих методов наличие первого параметра с характерным названием this. Т.е. что я сделал? Правильно, использовал один из паттернов, традиционно применяемых в ООП. А виндузовые handlers ничего не напоминают? Ну, например, ассоциации с паттерном инкапсуляция не напрашивается? А polymorphic behavior никто с помощью указателей на функции в C не делал? Я делал, очень удобно было реализовывать обработчики всевозможных событий, хотя даже само слово событие вошло в мой лексикон гораздо позже.
И что, после 15 лет все эти возможности куда-то испарились или стали невостребованными? Я думаю, что нет. Просто отдельные индивидуумы, выучив определение трёх основных составляющих ООП начинают пихать его во все нужные и ненужные места и потом искренне удивляются, почему это в ООП плохи дела с “параллельным программированием” и злобные основоположники парадигмы забыли решить “вопрос операций сразу на большим количеством похожих элементов данных”.
А может просто хватит заниматься фигнёй?
В общем, вывод может быть такой. У ООП есть только один недостаток – возможность использовать его не по назначению.
Но такие недостатки можно найти практически у любого предмета. Например, большой недостаток молотка заключается в том, что им очень больно попадать по пальцам, проблема лобзика в том, что им очень неудобно валить Беловежскую пущу, а к несомненным недостаткам выхлопной трубы можно отнести явное неудобство через неё регулировать клапана двигателя практически любого современного автомобиля.
И даже венец творения природы человек не лишён подобных недостатков. Видишь ли, не через любое отверстие в нём можно качественно удалить ему гланды
ЗЫ. Руки прочь от ООП!
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Maxim S. Shatskih, Вы писали:
MSS>1). ООшные дизайн паттерны плохо учитывают многонитевость и параллельное программирование.
Ты путашь отсуствие средств поддержки многозадачности в языках, библиотеках или фрэйворках с проблемами прадигмы. У ООП нет проблем с многозадачностью, так как она никак не противречит коцепции.
MSS>ООшное решение для такого — обернуть каждый элемент данных в объект, создать контейнер объектов, и потом итерировать контейнер. MSS>Кроме итераций, ОО практически ничего не предлагает.
Скажу больше в ООП нет и итерации.
ООП — это способность глядеть на мир через призму объектов и передачи между неми сообщений. Алгоритмические проблемы не относятся к области охватываемой ООП. Объекты могут быть целью алгоритма или данными обрабатываемыми алгоритмом, но это не одно и тоже.
Более того, ООП позволяет работать с данными как с абстракциями. Например, разумные люди вводят абстракцию словаря, инкапсулируют ее в объект и получают возможность быстро получать значение сопоставленное с ключем даже не задумываясь о том используется для этого итерация/рекурсия или вычисление хэш-фукнции.
Так что ты просто не понимашь что такое ООП и его цели. Ну, а далее как, сказал IT, как следствие получается забивание гвоздей микроскопом и возмущение от того, что это неудобно.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
IT>Честно говоря, надоели уже до чёртиков все эти разговоры про недостатки ООП.
Есть такое страшное слово — "парадигма". Достаточно нечто обозвать парадигмой, как вокруг этого нечто образуется своя толпа фанатов и фанатиков, с пеной у рта начинающих доказывать состоятельность и всеобъемность этой парадигмы другой толпе фанатов и фанатиков.
Сейчас, боюсь, силами С# начнут проталкивать функциональную парадигму.
Силами какой-нибудь Singularity — парадигму, что "все есть объект или процесс".
"И если увидишь где человека, слово 'парадигма', рекущего, перекрестись на все стороны света и плюнь через левое плечо два раза, а через правое — три, поелику есть то сам чорт и бес, вразумлению человеческому невнемлющий."
Здравствуйте, Maxim S. Shatskih, Вы писали:
MSS>Ну вот примерно так.
Нет. Это все к OOП мало отношения имеет. Уже описали.
У ООП есть один глобальный недостаток. Он дает видимость того, что с помощью ООП мы легко можем смоделировать реальный мир. Только это неправда. Человек не природа и у него ошибок слишком много чтобы это все легко повторить. Поэтому для моделирования нужно соблюдать еще правила дизайна. И за пределы возможностей компьютеров и программных языков выйти нельзя.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Maxim S. Shatskih, Вы писали:
VD>ООП — это способность глядеть на мир через призму объектов и передачи между неми сообщений. VD>Более того, ООП позволяет работать с данными как с абстракциями.
Я бы назвал способ смотреть на данные первоочередным для объектного подхода: данные наделются способностью делать что-то для нас, в отличие от действий над пассивными данными характерными для процедурного подхода.