D>Однако есть один момент. Когда ООП-ники говорят про преимущества ООП, одним из основных аргументов служит следующий: D>ОО проектирование легко позволяет моделировать окружающий мир. Видим в нём сотрудников — делаем класс Employee, видим начальника — делаем класс Manager, поскольку начальник is a сотрудник (в окружающем мире так) пользуем наследование.
А вот это как раз и неверно!
Если не ошибаюсь, такую формулировку ("ООП моделирует объекты реального мира") вводит Буч в своей книжке.
А вот умный Бертран Мейер, которого я продолжаю рекомендовать почитать, говорит иначе, примерно так (передаю смысл):
Какая, нах, модель реального мира? Какие объекты из реального мира моделирует компилятор? База данных? Или игра "тетрис"?
Программа реализует некоторую модель вашей предметной области, которая может иметь весьма мало общего с реальным миром.
Процедурный подход реализует модель "тетриса" процедурно. ООП — реализует ту же, по сути, модель, но объектно, что действительно удобно, т.к. конструкции языка ближе к структуре самой модели.
D>Так вот проблемы с квадратом и прямоугольником как раз показывают, что по большому счёту эта простая схема — впендюринг.
Думаю, даже не впендюринг, а реально заблуждение некоторых. Маленькая такая неточность, которая иногда может вылиться в большие проблемы (это вообще свойственно маленьким неточностям).
Кё>1. Построить иерархию классов для прямоугольника и квадрата, с изменяющимися размерами. Трудность в том, что хотя квадрат всегда является прямоугольником (наследуется), он не захочет наследовать его реализацию (зачем ему раздельно хранить ширину и высоту?). Другая трудность в том, что прямоугольник иногда может являться квадратом, но классические отношения между классами, которые предлагают ЯП (наследование и implicit-conversion) не могут выразить такого отношения.
Очень много проблем от того, что интуитивно вы смешиваете ту модель, которая вам нужна в программе с той моделью, которая у вас в голове, от реального мира, от геометрии, и т.п.
Вот про вторую надо забыть
Рассмотрим, что вам нужно в программе:
1) У любой фигуры есть положение (Location).
2) У фигуры "прямоугольник" задается ширина и высота. Это два независимых параметра, которые можно менять.
3) У фигуры "квадрат" задается размер стороны. Этот параметр можно менять.
Вот исходя из этих исходных данных можно совершенно определенно сказать, что "квадрат" никак не может являться "прямоугольником"!
Потому что у него нет двух независимых параметров "ширина" и "высота".
Откуда вы вообще взяли, что "квадрат всегда является прямоугольником"? Кто вам сказал этот бред?
Вот здесь кроется ошибка!
Вы это взяли из ДРУГОЙ МОДЕЛИ, из геометрии. Но там квадрат и прямоугольник — это СОВСЕМ ДРУГОЕ. Нечто, совсем непохожее, как бы вам не казалось на первый взгляд!
Там вообще нет таких понятий как "менять высоту", например Там это вообще — не объект с параметрами и поведением, а некое геометрическое место точек И с позиции тех свойств, в той модели, да, квадрат, определенно является прямоугольником. Но при чем тут ваша программа?!
Надеюсь, доходчиво объяснил, почему эта иерархия не трудна для ООП
Здравствуйте, Delight, Вы писали: D>Потому что у него есть и ширина, и высота. По мне так можно унаследовать квадрат от прямоугольника, переопределить сеттеры и при изменении одной стороны автоматически изменять другую.
Ребята, попользуйте поиск. Квадраты с прямоугольниками в ООП обсуждались на этом сайте досконально уже раза три. Все ответы, которые вы можете придумать, уже были даны.
Правильный ответ — такой: при проектировании иерархии не надо думать о свойствах. Думайте о поведении. Если речь идет о CAD или векторном граф.редакторе, у вас 100% не будет классов ни для квадратов, ни для прямоугольников. Если речь идет о системе для решения геометрических задач, то никаких сеттеров ни у кого не будет вообще.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Igor Trofimov, Вы писали:
iT>Очень много проблем от того, что интуитивно вы смешиваете ту модель, которая вам нужна в программе с той моделью, которая у вас в голове, от реального мира, от геометрии, и т.п. iT>Вот про вторую надо забыть
На всякий случай : я полностью согласен с поскипаным объяснением.
Однако есть один момент. Когда ООП-ники говорят про преимущества ООП, одним из основных аргументов служит следующий:
ОО проектирование легко позволяет моделировать окружающий мир. Видим в нём сотрудников — делаем класс Employee, видим начальника — делаем класс Manager, поскольку начальник is a сотрудник (в окружающем мире так) пользуем наследование.
Так вот проблемы с квадратом и прямоугольником как раз показывают, что по большому счёту эта простая схема — впендюринг.
Здравствуйте, mrozov, Вы писали:
M>DSL — это только один из вариантов развития. Затачивание языка под прикладную область/тип мышления. Было бы неплохо нащупать прорыв
DSL вообще не вариант. Проблема не в создании нового языка, и уж тем более узкоспециализированного. Проблема в эффективном использовании этого языка, а эффективность обеспечивается качественными компиляторами, IDE, отладчиками, библиотеками и людьми которые умеют язык использовать. Создание подобной инфраструктуры стоит десятки, сотни миллионов баксов. Эти деньги можно вложить только в мэйнстрим язык, и то вскладчину, всем миром. В подобный уровень поддержки DSL языка никто вкладываться не будет. А без такой инфраструктуры, хоть DSL и облегчает программирование в конкретной области, он как правило не может превысить эффективности языка общего назначения.
Прорыв известен уже давно — вместо программирования в синтаксических конструкциях надо программировать в семантических значениях. Самым разрекламированным в этом направлении является Intentional Programming (особо этому помогло то, что про его детали реализации никто ничего не знает, и все надеются на лучшее). Такой подход позволит легко определять онтологии для предметных областей (функции DSL), адаптировать имеющийся язык к конкретной задаче (расширение языка), легко адаптироваться к усложняющемуся железу и т.п.
Пока этот подход был не очень конкурентоспособен — поскольку предъявляет достаточно большие требования к сложности софта и железа разработчика. Так же как кислородное дыхание было невыгодно, по сравнению с анаэробным, до определённого уровня насыщенности воды/атмосферы кислородом.
Фактически, нынешние компьютеры разработчиков уже предоставляют достаточную мощность для семантического программирования, но они не могут пробиться в массы всё по тем-же причинам — экологическая ниша уже занята. Но ниши, в которых нынешний мэйнстрим не справляется — существуют. В этих нишках и прозябают нынешние DSLи. В них семантическое программирование и начнёт своё становление. А лет через 10-15, когда закончится действие закона Мура, и изготовители железа перейдут от погони за количеством (мегагерц, транзисторов, ядер) к погоне за качеством (сложностью, адаптивностью и т.п.) — нынейшний мэйнстрим не сможет предложить никаких вариантов, и будет почти полностью вытеснен.
Здравствуйте, deniok, Вы писали:
D>Однако есть один момент. Когда ООП-ники говорят про преимущества ООП, одним из основных аргументов служит следующий:
D>ОО проектирование легко позволяет моделировать окружающий мир. Видим в нём сотрудников — делаем класс Employee, видим начальника — делаем класс Manager, поскольку начальник is a сотрудник (в окружающем мире так) пользуем наследование.
Прикольно. Самое смешное, что я не могу вспомнить такого аргумента. Ну то есть я знаю, что это наверняка из-за того, что я читал мало книжек. Но вообще-то, основной аргумент за ООП никак не связан с моделированием реального мира. Основной аргумент — это борьба со связностью программы, благодаря инкапсуляции и полиморфизму. Вышеописанный подход — это какое-то неправильное ООП. В реальном программировании так никогда не делается. Где вы видели отнаследованного от сотрудника начальника?
В нормальном ООП, скорее всего, и сотрудник и начальник будут просто строчками в базе данных, при этом никто ни от кого не будет наследоваться, а будет связь между человеком и позицией, причем один-ко-многим, и будет интерфейс IHumanResources, отвечающий за назначение должностей и прочие простые штуки. И будет куча реализаций этого интерфейса — поверх XML файла для микроконтор, и поверх MS SQL Server Enterprize Edition для корпораций. И будет отнаследованный от него интерфейс типа IAdvancedHumanResources, в котором помимо базовой функциональности будет масса возможностей вроде поддержки организации проектных команд, планирования аттестаций и учета вакансий.
Очевидно, что ни один из этих классов не имеет никаких аналогов в реальном мире. Тем не менее, преимущества ООП по сравнению с, к примеру, процедурным программированием, также очевидны.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, jazzer, Вы писали:
J>Эта проблема, вообще-то, никакого отношения к ООП не имеет.
Согласен. IMHO большинство проблем не имеет отношения к ООП, и наоборот ООП не имеет отношения к большинству проблем. Имею ввиду именно проблемы программирования, а не проблемы вообще, так как к счастью никакой философской школы из ООП вроде не возникло, так что ОООПисты хоть непрограммистам мозги не мучают. Менеджерам разве еще...
Здравствуйте, Кодёнок, Вы писали:
Кё>Я ищу примеры классов объектов, которые вызывают трудности при объектном моделировании в современных ЯП типа C++ или C#. В качестве примера, я знаю всего две классические задачи:
Кё>1. Построить иерархию классов для прямоугольника и квадрата, с изменяющимися размерами. Трудность в том, что хотя квадрат всегда является прямоугольником (наследуется), он не захочет наследовать его реализацию (зачем ему раздельно хранить ширину и высоту?). Другая трудность в том, что прямоугольник иногда может являться квадратом, но классические отношения между классами, которые предлагают ЯП (наследование и implicit-conversion) не могут выразить такого отношения.
Кё>2. Комплексное число и вещественное число. Хотя вещественное число является комплексным (наследование), вы не станете наследовать его от комплексного по многим причинам: вам не нужна его реализация (два float в памяти вместо одного), вы вряд ли будете рады тому что sqrt(4) вместо 2.0 вернёт тупл из двух комплексных чисел, и т.п.
Кё>Также буду рад услышать названия языков, которые попытались реализовать более богатый набор отношений, чем даёт классическое понимание объектно-ориентированного проектирования.
Трудно ответить на вопрос, которые выдает с головой ваше непонимание смысла ООП, не в обиду будет сказано. Но я мужественно попробую
Для начала, иерархия классов — это увеличение сложности. Если взять три отдельных класса и три класса, объединяемых в иерархию, то последняя конструкция сложнее. Сопровождать и поддерживать иерархию гораздо сложнее отдельных классов — больше зависимостей и пр... Я думаю это понятно, можно не продолжать.
Но все-таки иерархии существуют. Потому что иногда, при определенных целях, грамотная иерархия позволяет уменьшить сложность.
Далее, иерархия классов — это способ решения задачи. А где у Вас задача-то? Вы задаете вопрос об иерархии и совершенно ничего не говорите о целях. Главный вопрос — зачем? Зачем нужно придумать искусственную конструкцию? Поскольку Вы не определились с целями, то и ответа нет.
Если Вы от _методов_ решения, переместите взгляд на _цели_ (задачи), то Вам станет легче жить. Например, цель может быть такая: унификация хранения. Или — унификация отображения. Или — унификация расчетов и т.д.
Допустим Вы ставите цель: унификация отображения геометрической фигуры.
Тут же Вы понимаете — не нужна Вам никакая иерархия. Все что Вам нужно, это:
interface
{
void draw(TCanvas canvas, RECT rect);
}
Причем это решение вообще не накладывает никаких ограничений на то, что именно рисуется — квадрат, круг или портрет Дориана Грея.
Вот и ответ на Ваш вопрос.
То же рассуждение касается и чисел. Когда Вы пытаетесь неутомимым программистским умом запихнуть в одну конструкцию вещественные и комплексные числа, Вы спросите себя — зачем? Зачем Вам знать сколько там float-ов в вещественном числе? Нет, правда — зачем? Вам беспокоит вопрос унификации хранения разнородных объектов? А почему? Вы хотите их на диск сбрасывать в записи фиксированной длины?
Основная цель ООП: снижение сложности программирования для программиста. Точка. Все остальные задачи, которые любят обсуждать на форумах являются ложными (мнимыми). Сюда относятся вопросы типа "надо ли считать собаку объектом или это набор интерфейсов", "отчего множественное наследование рождает столько проблем и как нам нравится на них натыкаться" и т.д.
Здравствуйте, Кодёнок, Вы писали:
Кё>Я ищу примеры классов объектов, которые вызывают трудности при объектном моделировании в современных ЯП типа C++ или C#.
Мне кажется, трудности во многом вызываются использованием единого механизма наследования для выражения совершнно разных отношений:
тип — подтип;
структура — расширение cтруктуры;
интерфейс — реализация;
интерфейс — расширеный интерфейс;
используемый интерфейс — реализация;
реализация — точно такая же, но с перламутровыми пуговицами;
...
Забавное обсуждение
Автор привел пару примеров задач, решение которых в рамках традиционного ООП является затруднительным, и попросил подкинуть еще (т.е. добавить убедительности, по сути).
А ему в ответ все дружно начали объяснять, как эти примеры нужно переписать, чтобы затруднений не было (т.е. решать прямо обратную задачу).
Понятно, что можно все красиво переделать, оставаясь в рамках ООП. Или функциональщины. Или процедурного программирования. Понятно, что любой алгоритм можно переписать на ассемблере или на Smalltalk-е, было бы желание.
Но все это ни в малейшей степени не означает, что приведенные примеры не являются затруднительными для решения. Идеальный язык не будет навязывать свою парадигму мышления, он будет логично встраиваться в систему взглядов эксперта предметной области. Я так понимаю, что речь именно об этом.
Здравствуйте, Кодт, Вы писали:
К>>ужно определиться с тем, что именно означает иерархия структур в каждом случае: это специализация (субклассирование) или расширение (наследование). К>К специализации применим LSP, к расширению — очевидно, нет.
Не совсем так. Нарушают LSP три формы наследования: наследование с целью реализации, наследование с целью обобщения и наследование с целью ограничения. Пример наследование с целью реализации — закрытое наследование в С++. Наследование с целью обобщения — это по сути перевернутое традиицонное наследование порождающее подтипы. Наследование с целью ограничения применяется когда необходимо ограничить возможности подкласса основываясь на уже существеющей иерархии, которую нет возможности менять. Наследование квадрата от прямоугольника попадает именно под эту форму наследования.
Наследование с целью расширения наряду со специализацией и спецификацией (поледнее является частным случаем специализации, например, наследование интерфейса) входит в список "правильных" форм наследования для которых выполняется LSP, то есть для которых подклассы всегда являются подтипами.
К>В Блоге Труъ Программиста в августе была серия статей на тему.
Я ищу примеры классов объектов, которые вызывают трудности при объектном моделировании в современных ЯП типа C++ или C#. В качестве примера, я знаю всего две классические задачи:
1. Построить иерархию классов для прямоугольника и квадрата, с изменяющимися размерами. Трудность в том, что хотя квадрат всегда является прямоугольником (наследуется), он не захочет наследовать его реализацию (зачем ему раздельно хранить ширину и высоту?). Другая трудность в том, что прямоугольник иногда может являться квадратом, но классические отношения между классами, которые предлагают ЯП (наследование и implicit-conversion) не могут выразить такого отношения.
2. Комплексное число и вещественное число. Хотя вещественное число является комплексным (наследование), вы не станете наследовать его от комплексного по многим причинам: вам не нужна его реализация (два float в памяти вместо одного), вы вряд ли будете рады тому что sqrt(4) вместо 2.0 вернёт тупл из двух комплексных чисел, и т.п.
Также буду рад услышать названия языков, которые попытались реализовать более богатый набор отношений, чем даёт классическое понимание объектно-ориентированного проектирования.
Здравствуйте, Кодёнок, Вы писали:
Кё>Я ищу примеры классов объектов, которые вызывают трудности при объектном моделировании в современных ЯП типа C++ или C#. В качестве примера, я знаю всего две классические задачи:
Нужно определиться с тем, что именно означает иерархия структур в каждом случае: это специализация (субклассирование) или расширение (наследование).
К специализации применим LSP, к расширению — очевидно, нет.
Благодаря тому, что в обычных языках наследование как технический инструмент позволяет и специализировать, и расширять класс (перекрытие/перегрузка интерфейсных функций; надстройка layout'а и изменение семантики на корню), возникает прекрасная почва для путаницы в сознании.
В Блоге Труъ Программиста в августе была серия статей на тему.
Кё>Также буду рад услышать названия языков, которые попытались реализовать более богатый набор отношений, чем даёт классическое понимание объектно-ориентированного проектирования.
Haskell и O'Haskell. В последнем — вопросам классовой борьбы уделяется много внимания.
Здравствуйте, Кодёнок, Вы писали:
Кё>Также буду рад услышать названия языков, которые попытались реализовать более богатый набор отношений, чем даёт классическое понимание объектно-ориентированного проектирования.
Кё>>... квадрат всегда является прямоугольником ...
I>Изменяемый квадрат не всегда является изменяемым прямоугольником. А в программировании в отличии от математики квадраты и прямоугольники как правило изменяемы.
Кстати, интересный факт из геометрии — квадрат, является не только прямоугольником, но и ромбом, параллелограммом и четырехугольником, а также возможно и трапецией (здесь я не уверен). Так что, судя по всему, объектная иерархия может существенно запутаться
Здравствуйте, nikov, Вы писали:
N>Здравствуйте, igna, Вы писали:
I>>P.S. Параллелограмм это трапеция
N>Это неверно. Смотреть любой учебник геометрии или здесь.
Как категорично. Предлагаю прочитать первый абзац в английской версии статьи.
P.S. Я обычно статьи (на тематику, не связанную с Россией) из русскоязычной википедии даже не читаю, когда мне ссылки дают на нее, переключаюсь на соответствующую англоязычную статью сразу.
Кё>1. Построить иерархию классов для прямоугольника и квадрата, с изменяющимися размерами. Трудность в том, что хотя квадрат всегда является прямоугольником (наследуется), он не захочет наследовать его реализацию (зачем ему раздельно хранить ширину и высоту?). Другая трудность в том, что прямоугольник иногда может являться квадратом, но классические отношения между классами, которые предлагают ЯП (наследование и implicit-conversion) не могут выразить такого отношения.
Кё>2. Комплексное число и вещественное число. Хотя вещественное число является комплексным (наследование), вы не станете наследовать его от комплексного по многим причинам: вам не нужна его реализация (два float в памяти вместо одного), вы вряд ли будете рады тому что sqrt(4) вместо 2.0 вернёт тупл из двух комплексных чисел, и т.п.
Мне кажется, что иерархия объектов — это не что-то объективно существующее, а всего лишь модель, которая позволяет нам более-менее адекватно описывать предметную область. Соответственно и примеры, данные выше, надо рассматривать в контексте области применения.
Вот предположим, что у нас есть прямоугольник, который может произвольным образом менять свои размеры, соответственно, максимум, что мы можем тут сделать — это ввести признак isSquare. Если же объекты у нас создаются один раз, а потом рисуются и метод рисования квадрата отличается от метода рисования прямоугольника (эффективнее), то класс квадрата и класс прямоугольника могут быть связаны только через общий класс-предок (например, фигура), и выбирать, объект какого класса должен быть создан должна соответствующая фабрика. Ну и так далее...
С комплексными числами то же самое. Подумайте, зачем они вам нужны и принимайте решение, исходя из этого.
Здравствуйте, konsoletyper, Вы писали:
D>>Потому что у него есть и ширина, и высота. По мне так можно унаследовать квадрат от прямоугольника, переопределить сеттеры и при изменении одной стороны автоматически изменять другую.
K>Наследование класса — это сахар. Да и определение класса — тоже сахар; по сути происходит описание интерфейса и его же реализация. Вот если иммутабельные Rectangle и Square реализуют интерфейс IRectange, то проблем не возникает. А зачем нужны мутабельные Rectangle и Square?
Если нужны мутабельные, то нужно делать мутабельные. Впрочем у меня эта боян-проблема-в-себе вызывает довольно вялый интерес. На флейм не пойду.
Здравствуйте, Кодёнок, Вы писали:
Кё>1. Построить иерархию классов для прямоугольника и квадрата, с изменяющимися размерами. Трудность в том, что хотя квадрат всегда является прямоугольником (наследуется), он не захочет наследовать его реализацию (зачем ему раздельно хранить ширину и высоту?). Другая трудность в том, что прямоугольник иногда может являться квадратом, но классические отношения между классами, которые предлагают ЯП (наследование и implicit-conversion) не могут выразить такого отношения.
Последнее отношение выполняется не всегда. Есть верное утверждение "квадрат? x => прямоугольник? x", т.е. любой квадрат можно представить как прямоугольник, но утверждение "прямоугольник? x => квадрат? x" в общем случае неверно, соответственно, никто по рукам бить не будет, если мы нарушим LSP. Так что можно обойтись явным приведением прямоугольника к квадрату, с выбросом исключения при необходимости.
Кё>2. Комплексное число и вещественное число. Хотя вещественное число является комплексным (наследование), вы не станете наследовать его от комплексного по многим причинам: вам не нужна его реализация (два float в памяти вместо одного), вы вряд ли будете рады тому что sqrt(4) вместо 2.0 вернёт тупл из двух комплексных чисел, и т.п.
А кто сказал, что нужно наследовать реализацию? С интерфейсами не возникает никаких проблем. В частности, sqrt для float и sqrt для complex — это, вообще-то, математически разные вещи, просто символ радикала является полиморфным (например, в той же математики делается различие между Ln и ln). Так что тут надо либо давать другое имя, либо делать explicit-реализацию метода IComplex.Sqrt, либо юзать язык с перегрузкой типов по возвращаемому значению.
Зато есть другая вещь, которую вот так просто в терминах "классового" ООП не выразишь. Например, поле комплексных чисел является алгебраически замкнутым, а поле действительных — нет. Или ещё пример: Z является кольцом, а R — полем. И что тут делать? Хочу заметить, что подобные вещи вообще в терминах "классовых" ООЯ неудобно записывать. Гораздо лучше в терминах type classes из Haskell. Или на худой конец, в динамике. Если выражать в статике, то получим множественные runtime проверки типов, что по сути та же динамака.
Кё>Также буду рад услышать названия языков, которые попытались реализовать более богатый набор отношений, чем даёт классическое понимание объектно-ориентированного проектирования.
Smalltalk, Self вроде бы теоретически больше позволяют, чем "классовые" ООЯ, хотя у них есть свои проблемы.
Здравствуйте, Кодёнок, Вы писали:
Кё>Это с какой стати? Вообще-то я моделирую предметную область с помощью средств языка программирования. С какого перепуга я должен забыть о предметной области?
Насколько я понял, Igor Trofimov говорит о том, что квадрат из геометрии (реального мира) не является объектом предметной области. Объект предметной области — квадрат с изменяющейся стороной.
Здравствуйте, COFF, Вы писали:
COF>Кстати, интересный факт из геометрии — квадрат, является не только прямоугольником, но и ромбом, параллелограммом и четырехугольником, а также возможно и трапецией (здесь я не уверен). Так что, судя по всему, объектная иерархия может существенно запутаться
Для этого есть множественное наследование. Но если вдруг в дополнение ко всем этим фигурам позже захочется поиметь еще и многоугольник, придется менять иерархию.
P.S. Параллелограмм это трапеция, а трапеция это четырехугольник. А еще и дельтоид есть.
Здравствуйте, Кодёнок, Вы писали:
Кё>Я ищу примеры классов объектов, которые вызывают трудности при объектном моделировании в современных ЯП типа C++ или C#. В качестве примера, я знаю всего две классические задачи:
ИМХО и в первом и во втором случае имеем неправильную иерархию. Уже писал когда-то по поводу квадрата здесь
Комплексные и обычные числа — тоже самое:
Наследоваться нужно от класса 'число' (если уж вообще возникла необходимость в наследовании). Далее их можно разделить на всякие — натуральные, мнимые, действительными, положительные и т.д. Соответственно 'обычные' чиcла являются действительными, а комплексные — мнимыми.
Все описанные тобой проблемы ИМХО из-за того, что неправильно иерархии строят. Если уж строить, то строить по аналогии со школьным учебником, а там иерархии гораздо сложнее, чем в твоем примере. И тогда построить непротиворечивую эффективную иерархию не проблема, только несколько более громоздкой она получится.
Здравствуйте, elmal, Вы писали:
E>Наследоваться нужно от класса 'число' (если уж вообще возникла необходимость в наследовании). Далее их можно разделить на всякие — натуральные, мнимые, действительными, положительные и т.д. Соответственно 'обычные' чиcла являются действительными, а комплексные — мнимыми.
То есть предлагаешь иерархию использовать только для классификации их всех как "чисел", а автоматическую совместимость между ними реализовать через implicit conversion?
Здравствуйте, Курилка, Вы писали:
C>>P.S. OOP stinks К>А вот статейка на эту тему
Что-то не очень убедительная статья. Есть заявления в стиле "так вот, на самом деле это плохо и должно быть наоборот", но нет аргументов, почему. Безапелляционные утверждения — не лучший способ убедить.
Здравствуйте, Cephalopod, Вы писали:
Кё>>Также буду рад услышать названия языков, которые попытались реализовать более богатый набор отношений, чем даёт классическое понимание объектно-ориентированного проектирования.
C> Coq. HOL. Axiom.
Здравствуйте, Кодёнок, Вы писали:
Кё>Здравствуйте, Курилка, Вы писали:
C>>>P.S. OOP stinks К>>А вот статейка на эту тему
Кё>Что-то не очень убедительная статья. Есть заявления в стиле "так вот, на самом деле это плохо и должно быть наоборот", но нет аргументов, почему. Безапелляционные утверждения — не лучший способ убедить.
Почему безапелляционные?
Возьмём 4-й пункт "У объектов есть private состояние" — с самим утверждением не будешь спорить, надеюсь?
Джо говорит, что состояние — одна из проблем, состояние увеличивает зависимости, уменьшает расспараллеливаемость и т.д.
"Обычные" языки пытаются "спрятать" состояние (с помощью модификаторов и т.п.), тогда как в декларативных функциональных есть подаваемые данные на вход и результат на выходе.
И что здесь тебе показалось "безапелляционным"?
Скорее твоё утверждение таковым назвать следует
Здравствуйте, Кодёнок, Вы писали:
Кё>То есть предлагаешь иерархию использовать только для классификации их всех как "чисел", а автоматическую совместимость между ними реализовать через implicit conversion?
В общем да, а вот в деталях может оказаться что и нет . От деталей реализации все зависит.
Кё>А как решить проблему sqrt?
А какая проблема с sqrt? sqrt(Number number) и должен возвращать набор чисел. sqrt(4) = 2 и -2 насколько я помню. И собственно когда мы его будем возвращать — эти числа должно быть наиболее адекватного типа. Ну а sqrt(-1) должен возвратить комплексное число, вроде оно наже одно (блин, уже школьную программу забываю — ужас). Если для корня определенного числа возвратится несколько значений разных типов (например одно комплексное, другое действительное), а мне нужен допустим только действительные — я могу лишние отбросить. Я бы например сделал, чтобы sqrt возвращал объект класса 'Корни'. И у этого класса можно сделать методы фильтрации по содержимому — возвратить только положительные, возвратить только действительные и т.д, возвратить как список, как массив, получить итератьр и т.д.
Кё>1. Построить иерархию классов для прямоугольника и квадрата, с изменяющимися размерами. Трудность в том, что хотя квадрат всегда является прямоугольником (наследуется), он не захочет наследовать его реализацию (зачем ему раздельно хранить ширину и высоту?).
Почему прямоугольник должен ХРАНИТЬ ширину и высоту?
Кё>1. Построить иерархию классов для прямоугольника и квадрата, с изменяющимися размерами. Трудность в том, что хотя квадрат всегда является прямоугольником (наследуется), он не захочет наследовать его реализацию (зачем ему раздельно хранить ширину и высоту?).
Почему прямоугольник должен ХРАНИТЬ ширину и высоту?
Здравствуйте, Кодёнок, Вы писали:
Кё>Я ищу примеры классов объектов, которые вызывают трудности при объектном моделировании в современных ЯП типа C++ или C#. В качестве примера, я знаю всего две классические задачи:
Кё>1. Построить иерархию классов для прямоугольника и квадрата, с изменяющимися размерами. Трудность в том, что хотя квадрат всегда является прямоугольником (наследуется), он не захочет наследовать его реализацию (зачем ему раздельно хранить ширину и высоту?). Другая трудность в том, что прямоугольник иногда может являться квадратом, но классические отношения между классами, которые предлагают ЯП (наследование и implicit-conversion) не могут выразить такого отношения.
Кё>2. Комплексное число и вещественное число. Хотя вещественное число является комплексным (наследование), вы не станете наследовать его от комплексного по многим причинам: вам не нужна его реализация (два float в памяти вместо одного), вы вряд ли будете рады тому что sqrt(4) вместо 2.0 вернёт тупл из двух комплексных чисел, и т.п.
3. List<String> и List<Object>.
Вообще вышеприведённые примеры показывают трудности объектного моделирования как такового, а не просто в современных языках.
Кё>Также буду рад услышать названия языков, которые попытались реализовать более богатый набор отношений, чем даёт классическое понимание объектно-ориентированного проектирования.
А какие ещё отношения ты знаешь?
Идея наследования — это идея расширения (или сужения, в зависимости от ко- или контр-вариантного наследования) интерфейса, сиречь операций которые можно произвести с данным объектом. Какое ещё действие по отношению к интерфейсу ты можешь себе вообразить (кроме операции добавить/убавить набор операций)? Перетасовать их?
Здравствуйте, mkizub, Вы писали:
Кё>>Также буду рад услышать названия языков, которые попытались реализовать более богатый набор отношений, чем даёт классическое понимание объектно-ориентированного проектирования.
M>А какие ещё отношения ты знаешь?
M>Идея наследования — это идея расширения (или сужения, в зависимости от ко- или контр-вариантного наследования) интерфейса, сиречь операций которые можно произвести с данным объектом. Какое ещё действие по отношению к интерфейсу ты можешь себе вообразить (кроме операции добавить/убавить набор операций)? Перетасовать их?
Ну в Haskell есть Multi-parameter type classes:
-- Интерфейс, задающий отношение изоморфизма между типамиclass Iso a b where
iso :: a -> b
osi :: b -> a
-- Пример реализацииinstance Iso a a where
iso = id
osi = id
Фишка в том, что интерфейс выставляется не одним типом, а двумя. VTBL приклеивается не к "объекту", а к методам интерфейса.
Здравствуйте, Igor Trofimov, Вы писали:
iT>Очень много проблем от того, что интуитивно вы смешиваете ту модель, которая вам нужна в программе с той моделью, которая у вас в голове, от реального мира, от геометрии, и т.п. iT>Вот про вторую надо забыть
Это с какой стати? Вообще-то я моделирую предметную область с помощью средств языка программирования. С какого перепуга я должен забыть о предметной области?
Здравствуйте, Кодёнок, Вы писали:
Кё>Я ищу примеры классов объектов, которые вызывают трудности при объектном моделировании в современных ЯП типа C++ или C#. В качестве примера, я знаю всего две классические задачи:
Кё>1. Построить иерархию классов для прямоугольника и квадрата, с изменяющимися размерами. Трудность в том, что хотя квадрат всегда является прямоугольником (наследуется), он не захочет наследовать его реализацию (зачем ему раздельно хранить ширину и высоту?). Другая трудность в том, что прямоугольник иногда может являться квадратом, но классические отношения между классами, которые предлагают ЯП (наследование и implicit-conversion) не могут выразить такого отношения.
Хотя квадрат и является частным случаем прямоугольника, это не значит, что выгодно его наследовать от прямоугльника.
Скорее наоборот. Square : Figure { width }; Rectangle : Figure { width, height }; Figure { location, rotation, scale };
Тут замечаем, что у фигур есть общие поля. Теперь мы можем:
либо (1) завести для них общий базовый класс with_width { width };
И тогда Square : Figure, with_width {}; Rectangle : Figure, with_width { height };
либо (2) отнаследовать прямоугольник от квадрата на прямую Square : Figure { width }; Rectangle : Figure, Square { height };
Но эти решения не всегда эффективны. В случае графического редактора может быть выгоднее другое.
У фигур есть параметры. Параметры бывают разных типов, но у всех у них есть имя.
Кроме того фигуры бывают разных типов. Для типов заводим базовый абстрактный класс Prototype.
Для сериализации картинки, у типов фигур должны быть имена.
// параметры
interface IParam
{};
struct text_param : IParam
{};
struct number_param : IParam
{};
// ПараметрЫ. указатель на список параметров ( на хэш )typedef map<string, IParam *> * Params;
// фигура (не абстрактный класс!)struct Figure
{
Prototype * htype;
Params hparams;
};
Figure::constructor(Prototype * hnew_type)
{
htype= hnew_type;
hparams= htype->get_default_params();
}
Figure::draw()
{
htype->draw(this);
}
Figure::serialize(Archive & ar)
{
// сериализим имя типа фигуры
// сериализим список параметров
}
// прототипы фигурstruct Prototype abstract
{
virtual draw(Figure *) abstract;
virtual Params get_default_params() abstract;
virtual string get_name() abstract;
};
// прототип прямоугольникаstruct type_rect : Prototype
{
draw(Figure * hf)
{ /*рисуем прямоугольник*/ }
// другие методы
};
// тут нужны другие типы фигур ...
// картинкаstruct Picture
{
// тут список фигур
// методы:
draw(); // рисуем фигуры
serialize(...); // сериализим фигуры
add_figure(...);
del_figure(...);
// и т.д.
};
Квадрат имеет один параметр "сторона" ("side");
У прямоугольника "width", "height";
У круга "radius";
Фигура в этой схеме имеет указатели на тип и на хэш параметров для того, чтобы можно было
конвертировать одну фигуру в другую. К примеру квадрат преобразовать в прямоугольник.
Тут не фигура сама себя рисует. За действия над фигурами отвечают их прототипы.
Таким образом типы фигур не hard-coded, а могут загружаться из библиотек типов.
т.е. Тип фигуры можно тоже сериализовать.
Здравствуйте, Кодёнок, Вы писали:
Кё>1. Построить иерархию классов для прямоугольника и квадрата, с изменяющимися размерами. Трудность в том, что хотя квадрат всегда является прямоугольником (наследуется), он не захочет наследовать его реализацию (зачем ему раздельно хранить ширину и высоту?).
Потому что у него есть и ширина, и высота. По мне так можно унаследовать квадрат от прямоугольника, переопределить сеттеры и при изменении одной стороны автоматически изменять другую.
Здравствуйте, Delight, Вы писали:
D>Потому что у него есть и ширина, и высота. По мне так можно унаследовать квадрат от прямоугольника, переопределить сеттеры и при изменении одной стороны автоматически изменять другую.
Наследование класса — это сахар. Да и определение класса — тоже сахар; по сути происходит описание интерфейса и его же реализация. Вот если иммутабельные Rectangle и Square реализуют интерфейс IRectange, то проблем не возникает. А зачем нужны мутабельные Rectangle и Square?
Здравствуйте, Кодёнок, Вы писали:
Кё>1. Построить иерархию классов для прямоугольника и квадрата, с изменяющимися размерами.
Для решения какого класса задач нужна такая иерархия?
Кё>2. Комплексное число и вещественное число.
Опять-таки: для решения какого класса задач нужна такая иерархия?
Кё>Также буду рад услышать названия языков, которые попытались реализовать более богатый набор отношений, чем даёт классическое понимание объектно-ориентированного проектирования.
Предположим, такой язык нашелся. Ваши дальнейшие действия? Чем это Вам поможет при решении конкретных задач?
Здравствуйте, mkizub, Вы писали:
M>А какие ещё отношения ты знаешь?
Например то, что в некоторых языках реализовано как трейты (классы типов):
trait Addable[ThisType] { def +(ThisType other) }
Позволяет классифицировать как Addable любой тип, в том числе будущий или не подозревающий о существовании Addable. В классическом понимании ООП ты должен построить иерархию сразу, унаследовав всё что нужно от Addable. Если ты берешь стороннюю библиотеку (типичная ситуация при компонентной разработке), её иерархия не может включать твой класс Addable, и таким образом ты имеешь классы, про которые невооруженным взглядом видно что они Addable, но средства языка вроде C# не позволят тебе работать с ними, там, где требуется Addable.
Или отношение, которое сейчас принято реализовать паттерном прокси. Например, если есть некий IString, и есть классы CString и std::string, которые очевидно могут являться IString, но тот же C++ не позволит тебе добавить еще один интерфейс к уже готовым классам. А в идеальном варианте я бы дописал что-то вроде
implementation IString for std::string {
char GetChatAt(int i) { return at(i); }
}
Здравствуйте, Кодёнок, Вы писали:
M>>А какие ещё отношения ты знаешь?
Кё>Например то, что в некоторых языках реализовано как трейты (классы типов):
Кё>Или отношение, которое сейчас принято реализовать паттерном прокси.
Угу. А ещё есть возможность динамически добавлять методы и поля (Python и прочие динамические языки), динамически менять супер-класс (наследование делегированием сообщений). Есть ещё возможность автоматического приведения типов (вроде view в Scala, или просто преобразования int->float в С).
Но они никак не помогут решить проблему Квадрат-Прямоугольник или Complex-Float-Integer. Первая — просто неправильно построенное наследование, и Квадрат с Прямоугольником должны, скорее, унаследоваться от Фигура (как и Треугольник, Многоугольник, Круг и пр.) — так же как это сделано для byte, int, long, float, double в процедурных языках — они независимы, а не образуют иерархию. А с набором независимых типов есть проблема, заключающаяся в необходимости их знать все заранее. Если ты добавишь к числам complex — то отгребёшь по самое нехочу. А потом ещё можно добавить BigInteger/BigFloat, с неограниченным количеством бит. А если complex и BigFloat добавлены независимо, отдельно друг от друга? Ведь надо-бы иметь BigComplex, а иначе вся система рушится — ведь чему будет равен результат умножения BigFloat * complex?
При фиксированной задаче мы можем знать набор требований, и можем выбрать иерархию Квадрат->Прямоугольник или Прямоугольник->Квадрат или Фигура->Прямоугольник|Квадрат — в зависимости от той модели, которую нам надо реализовать. Но эта модель будет всегда ограничена. А неограниченная (универсальная) модель автоматически приводит к проблеме несовместимости расширений (как complex*BigFloat).
И это не проблема ООП. В функциональщине те-же проблемы. Так называемая Expression Problem. Вот в Scala её Мартин Одерски предлагает решать как здесь. По моему, очень коряво. Но альтернативные решения не лучше
Здравствуйте, elmal, Вы писали:
E>Здравствуйте, Кодёнок, Вы писали:
Кё>>То есть предлагаешь иерархию использовать только для классификации их всех как "чисел", а автоматическую совместимость между ними реализовать через implicit conversion? E>В общем да, а вот в деталях может оказаться что и нет . От деталей реализации все зависит.
Кё>>А как решить проблему sqrt? E>А какая проблема с sqrt? sqrt(Number number) и должен возвращать набор чисел. sqrt(4) = 2 и -2 насколько я помню. И собственно когда мы его будем возвращать — эти числа должно быть наиболее адекватного типа. Ну а sqrt(-1) должен возвратить комплексное число, вроде оно наже одно (блин, уже школьную программу забываю — ужас).
Здравствуйте, Кодёнок, Вы писали:
Кё>... квадрат всегда является прямоугольником ...
Изменяемый квадрат не всегда является изменяемым прямоугольником. А в программировании в отличии от математики квадраты и прямоугольники как правило изменяемы.
M>Забавное обсуждение M>Автор привел пару примеров задач, решение которых в рамках традиционного ООП является затруднительным, и попросил подкинуть еще (т.е. добавить убедительности, по сути).
M>Но все это ни в малейшей степени не означает, что приведенные примеры не являются затруднительными для решения. Идеальный язык не будет навязывать свою парадигму мышления, он будет логично встраиваться в систему взглядов эксперта предметной области. Я так понимаю, что речь именно об этом.
Так ведь суть в том, что это не задачи, а вопросы из серии — что было раньше, курица или яйцо, или с какого конца надо разбивать яйцо? А ведь эти проблемы легкими для решения тоже не назовешь. Другой вопрос, стоит ли их решать?
Здравствуйте, Кодёнок, Вы писали:
Кё>1. Построить иерархию классов для прямоугольника и квадрата, с изменяющимися размерами. Трудность в том, что хотя квадрат всегда является прямоугольником (наследуется), он не захочет наследовать его реализацию (зачем ему раздельно хранить ширину и высоту?). Другая трудность в том, что прямоугольник иногда может являться квадратом, но классические отношения между классами, которые предлагают ЯП (наследование и implicit-conversion) не могут выразить такого отношения.
Одинаковый интерфейс, разные реализации (данные представлены по-разному).
Тут все просто.
public interface IRectangle
{
double Width { get; }
double Height { get; }
}
public class Rectangle : IRectangle
{
readonly double _width;
readonly double _height;
public double Width { get { return _width; } }
public double Height { get { return _width; } }
}
public class Square : IRectangle
{
readonly double _size;
public double Size { get { return _size; } }
public double Width { get { return Size; } }
public double Height { get { return Size; } }
}
M>Идеальный язык не будет навязывать свою парадигму мышления, он будет логично встраиваться в систему взглядов эксперта предметной области.
Ох как красиво сказал! Утопично, но как звучит!
Здравствуйте, mrozov, Вы писали:
M>Но все это ни в малейшей степени не означает, что приведенные примеры не являются затруднительными для решения. Идеальный язык не будет навязывать свою парадигму мышления, он будет логично встраиваться в систему взглядов эксперта предметной области.
Система взглядов эксперта в предметной области может оказаться довольно наивной и слабосовместимой с системой взглядов эксперта другой области. Если уж мы допускаем эксперта до ЯП, то это должен быть специализированный (e)DSL + визуальная среда, а никак не язык общего назначения. Что мы собственно и видим: VBA для офисных автоматизаторов, TeX/LaTeX для писателей научных статей, AutoLisp для труженников AutoCad, SQL для БДкопателей, встроенные языки математических пакетов для научных расчетов.
В языке общего назначения всегда слишком много инструментов для поддержки собственно "програмистских" абстракций, которые предметникам совершенно не нужны.
Здравствуйте, deniok, Вы писали:
D>Однако есть один момент. Когда ООП-ники говорят про преимущества ООП, одним из основных аргументов служит следующий:
D>ОО проектирование легко позволяет моделировать окружающий мир.
Так а в реальном мире прямоугольники и квадраты неизменяемые!
Ты не можешь в геомтерии, из которой ты несешь идею "квадрат — это прямоугольник", изменить размеры ни квадрата, ни прямоугольника. Ты можешь только породить новый объект где-нть рядом ("по построению").
В таком read-only интерфейсе между ними действительно есть строгое отношение "is a", и все операции, которые ты можешь применить в прямоугольнику (т.е. получение размеров, вычисление площади, получение координат вершин и т.п.) применимы и к квадрату.
D>Видим в нём сотрудников — делаем класс Employee, видим начальника — делаем класс Manager, поскольку начальник is a сотрудник (в окружающем мире так) пользуем наследование.
Сорри, поясни, в чем тут проблема?
Я вот не вижу навскидку, что мы такого не можем сделать с сотрудником, чего не можем сделать с его начальником.
Нанять/уволить можем? Можем.
Зарплату заплатить или не заплатить можем? Можем.
Дать людей в подчинение можем? Можем.
Перекинуть его к другому начальнику можем? Можем.
В чем проблема-то?
D>Так вот проблемы с квадратом и прямоугольником как раз показывают, что по большому счёту эта простая схема — впендюринг.
Эти примеры показывают, что ты берешь реальный мир из геометрии, в котором квадрат является прямоугольником, перемешиваешь его с виртуальным миром Visio/PowerPoint, в котором ты можешь менять размеры и того, и другого, и при этом не отсекаешь этого перехода и продолжаешь думать в терминах read-only геометрии.
Это называется просто подменой понятий и к ООП отношение имеет опосредованное.
Здравствуйте, Кодёнок, Вы писали:
Кё>2. Комплексное число и вещественное число. Хотя вещественное число является комплексным (наследование), вы не станете наследовать его от комплексного по многим причинам: вам не нужна его реализация (два float в памяти вместо одного), вы вряд ли будете рады тому что sqrt(4) вместо 2.0 вернёт тупл из двух комплексных чисел, и т.п.
Эта проблема, вообще-то, никакого отношения к ООП не имеет.
В математике корень степени N из любого положительного числа на множестве действительных чисел будет возвращать действительное число, а на поле комплексных — N комплексных чисел.
Соответственно, в математике одноименные функции над действитальными и комплексными числами записываются с маленькой и большой буквы соответственно, если мне память не изменяет, конечно.
Здравствуйте, nikov, Вы писали:
N>Это неверно. Смотреть любой учебник геометрии или здесь.
В моем учебнике геометрии параллелограмм был частным случаем трапеции. Почитав русскую Википедию подумал сначала, что определение просто небрежно написано, но почитав английскую с удивлением обнаружил, что бывает и такое, исключающее частный случай параллелограмма, определение трапеции. Какой в нем смысл — для меня загадка, ведь вроде все доказательства и алгоритмы применимые к трапециям будут применимы и к параллелограммам, так к чему исключение? Подобные штуки в каком-нибудь языке программирования или в написанной на нем библиотеке меня скорее всего не удивили бы; но у математиков как правило все намного лучше продумано.
История величайших изобретений (не путать с открытиями) состоит из восклицаний "почему я раньше до этого не додумался".
ООП — явно не вершина человеческой мысли. Это всего лишь инструмент ее работы, оказавшийся удобным на конкретном участке траектории. Но уже достаточно ясно, что нужно двигаться не просто дальше — а в сторону. Нужен другой инструмент, который позволит программистам выражать свои мысли более простым и естественным способом.
DSL — это только один из вариантов развития. Затачивание языка под прикладную область/тип мышления. Было бы неплохо нащупать прорыв
Кё>Я ищу примеры классов объектов, которые вызывают трудности при объектном моделировании в современных ЯП типа C++ или C#.
С трудностями не встречался ни разу. Нужно просто знать некоторые приёмчики (паттерны и просто трюки) и правильно анализировать предметную область.
Кё>1. Построить иерархию классов для прямоугольника и квадрата, с изменяющимися размерами. Трудность в том, что хотя квадрат всегда является прямоугольником (наследуется), он не захочет наследовать его реализацию (зачем ему раздельно хранить ширину и высоту?). Другая трудность в том, что прямоугольник иногда может являться квадратом, но классические отношения между классами, которые предлагают ЯП (наследование и implicit-conversion) не могут выразить такого отношения.
Решение тривиально после правильного анализа, который заключается в том, что необходимо выделить интерфейс или абстрактный класс (как уже отмечали выше):
public abstract class Figure {
// координаты центра масс и
// угол поворота от некоторого положения по умолчанию (для прямоугольников - когда стороны параллельны осям)float x,y,a;
abstract Figure rotate(float da);
abstract Figure move(float dx, float dy);
abstract Figure resize(float factor); // операция изменения размера производит подобную фигуруabstract void paint(Graphics g);
}
public abstract class Rectangle {
// размеры: при угле равном 0, стороны соотв. ширине, параллельны оси OXabstract float getWidth();
abstract float getHeight();
abstract Rectangle move(float dx, float dy);
// операция изменения размера производит подобную фигуру,
// поэтому мы можем уточнить типabstract Rectangle resize(float factor);
abstract Rectangle resize(float wfactor, hfactor);
...
}
public class GenericRectangle extends Rectangle {
float x, y, a;
float width, height;
Rectangle resize(float factor) { return new GenericRectangle(x, y, a, width *factor, height * factor) }
Rectangle resize(float wfactor, float hfactor) { return new GenericRectangle(x, y, a, width * wfactor, height * hfactor) }
...
}
public class Square extends Rectangle {
float x,y,a,size;
Square resize(float factor) { return new Square(x,y,a,size*factor); }
Rectangle resize(float wfactor, hfactor) {
if (wfactor == hfactor) {
return new Square(x,y,a,size*wfactor);
} else {
return new GenericRectangle();
}
}
}
Кё>2. Комплексное число и вещественное число. Хотя вещественное число является комплексным (наследование), вы не станете наследовать его от комплексного по многим причинам: вам не нужна его реализация (два float в памяти вместо одного), вы вряд ли будете рады тому что sqrt(4) вместо 2.0 вернёт тупл из двух комплексных чисел, и т.п.
Аналогично, реализация отделяется от наследования. Операции над числами реализуются в соответствующей алгебре:
public interface Number {
double[] getTuple(); // возвращает 1,2 или 4 элемента (если угодно поддерживать экзотику, то можно 8 и 16)double get(int component); // возвращает значение соотв. компонента
}
public interface ComplexNumber implements Number {
abstract double re();
abstract double im();
}
public class GenericComplexNumber implements ComplexNumber {
double[] tuple = new double[2];
}
public class RealNumber implements ComplexNumber {
double[] tuple = new double[2];
public double value() { return tuple[0]; }
}
public interface Algebra<N extends Number> {
Number[] basis();
double multiply(N n1,N n2);
double add(N n1,N n2);
... // и другие операции для данной алгебры
}
public class ComplexAlgebra extends Algebra<ComplexNumber> {
ComplexNumber[] sqrt(ComplexNumber number) { ... }
}
public class RealAlgebra extends Algebra<RealNumber> {
RealNumber sqrt(RealNumber number) { ... }
}
Здравствуйте, jazzer, Вы писали:
J>Здравствуйте, Кодёнок, Вы писали:
Кё>>2. Комплексное число и вещественное число. Хотя вещественное число является комплексным (наследование), вы не станете наследовать его от комплексного по многим причинам: вам не нужна его реализация (два float в памяти вместо одного), вы вряд ли будете рады тому что sqrt(4) вместо 2.0 вернёт тупл из двух комплексных чисел, и т.п.
J>Эта проблема, вообще-то, никакого отношения к ООП не имеет.
J>В математике корень степени N из любого положительного числа на множестве действительных чисел будет возвращать действительное число, а на поле комплексных — N комплексных чисел. J>Соответственно, в математике одноименные функции над действитальными и комплексными числами записываются с маленькой и большой буквы соответственно, если мне память не изменяет, конечно.
Здравствуйте, igna, Вы писали:
I>Здравствуйте, Кодёнок, Вы писали:
Кё>>... квадрат всегда является прямоугольником ...
I>Изменяемый квадрат не всегда является изменяемым прямоугольником. А в программировании в отличии от математики квадраты и прямоугольники как правило изменяемы.
В книжке Шлеер и Меллора было понятие "миграция подтипа", насколько я помню. Т.е. объект может стать принадлежащим другому классу
Здравствуйте, igna, Вы писали:
I>Если не ошибаюсь, у тебя все объекты неизменяемые, так?
Ну да, в первом примере он сделал методы, которые выводят экземпляр подкласса из этого подкласса константными, по другому никак, это единственное решение, не нарушающее принцип подстановки Лисков. Правильнее было бы вообще их наследованием не связывать, правда. Но это неважно в данном случае, решение все равно корректно.
Во втором случае — с числами — решение просто неверно. Интерфейсы, которые он определил, не содержат операций, зато они дают полный доступ к атрибутам. Определенные им типы вообще не являются абстрактными типами данных, и принцип подстановки Лисков нарушается на раз-два-три. Правильное решение — сделать абстрактный класс "поле чисел", как в алгебре, и опять — не связывать оба типа чисел наследованием.
И третье — все решения переусложнены, и вводят больше сущностей, чем необходимо.
Здравствуйте, Курилка, Вы писали:
К>Почему безапелляционные? К>Возьмём 4-й пункт "У объектов есть private состояние" — с самим утверждением не будешь спорить, надеюсь? К>Джо говорит, что состояние — одна из проблем, состояние увеличивает зависимости, уменьшает расспараллеливаемость и т.д. К>"Обычные" языки пытаются "спрятать" состояние (с помощью модификаторов и т.п.), тогда как в декларативных функциональных есть подаваемые данные на вход и результат на выходе.
Четвертый пункт — единственный, с которым можно согласиться. Все прочие пункы — просто утверждения.
Почему "Since functions and data structures are completely different types of animal it is fundamentally incorrect to lock them up in the same cage."? А ОО утверждает, что все объекты реального мира имеют и состояние и поведенеие, и почему их надо программировать иначе?
"But in a non OO language a "time" is a instance of a data type." — ну и что с того? А ОО языке — это объект. И что с этого?
"Note that these definitions do not belong to any particular object. they are ubiquitous and data structures representing times can be manipulated by any function in the system." — а с ОО что не так? Использование вовсе != наследование.
"In an OOPL I have to choose some base object in which I will define the ubiquitous data structure, all other objects that want to use this data structure must inherit this object." — ну а это вообще бред.
квадрат может находиться в произвольном количестве контейнеров и композитных фигур,
при изменении размеров нужно менять экземпляр объекта в каждом контейнере
как это будет выглядеть в коде? сможет квадрат сам подменить себя?
Здравствуйте, Gaperton, Вы писали:
G>Здравствуйте, igna, Вы писали:
I>>Если не ошибаюсь, у тебя все объекты неизменяемые, так?
G>Ну да, в первом примере он сделал методы, которые выводят экземпляр подкласса из этого подкласса константными, по другому никак, это единственное решение, не нарушающее принцип подстановки Лисков. Правильнее было бы вообще их наследованием не связывать, правда.
Почему, если не секрет?
Как ты в языках типа С++ заставишь функцию, работающую с Figure, принимать объекты Rectangle и прочих, если они не связаны отношением наследования (т.е. абсолютно разные типы)?
Здравствуйте, madlax, Вы писали:
M>Baudolino,
M>квадрат может находиться в произвольном количестве контейнеров и композитных фигур, M>при изменении размеров нужно менять экземпляр объекта в каждом контейнере M>как это будет выглядеть в коде? сможет квадрат сам подменить себя?
Совершенно непонятно, что такое "находиться в контейнере", и что такое "изменение размеров".
Если речь идет о квадрате с семантикой value-типа (к которой вы привыкли в геометрии), то никакого "изменения размеров" у него не может быть.
Квадраты-значения ведут себя так же, как целые числа. Давайте перефразируем ваш вопрос:
целое может находиться в произвольном количестве контейнеров и композитных фигур,
при изменении значения нужно менять экземпляр объекта в каждом контейнере
как это будет выглядеть в коде? сможет целое сам подменить себя?
И попробуем представить пример. Каким значением вы собираетесь заменить число "пять"? Что будет с программой, в которой все пятерки превратятся в тройки?
Если речь идет о CAD системе, то никакого value-type поведения у квадратов не будет. Квадрат окажется частным случаем ломаной; никаких чудес с взаимонаследованием от прямоугольников не произойдет, т.к. поведение квадрата как части объектной модели не будет накладывать никаких ограничений ни на углы, ни на равенство сторон, ни даже на их количество. В кадах применяется совсем другая алгебра; там есть примитивы merge и subtract, есть примитив group и так далее. Никакие "геометрические" свойства не будут инвариантами, и наследование не будет иметь никакого смысла.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, jazzer, Вы писали:
J>Здравствуйте, Gaperton, Вы писали:
G>>Здравствуйте, igna, Вы писали:
I>>>Если не ошибаюсь, у тебя все объекты неизменяемые, так?
G>>Ну да, в первом примере он сделал методы, которые выводят экземпляр подкласса из этого подкласса константными, по другому никак, это единственное решение, не нарушающее принцип подстановки Лисков. Правильнее было бы вообще их наследованием не связывать, правда.
J>Почему, если не секрет? J>Как ты в языках типа С++ заставишь функцию, работающую с Figure, принимать объекты Rectangle и прочих, если они не связаны отношением наследования (т.е. абсолютно разные типы)?
J>Или все на шаблонах будешь делать?
Надо квадрат с прямоугольником наследованием не связывать. Толку от этого ноль в реальной ситуации, только проблемы. А вот от какого-то абстрактного Figure и то и другое наследовать можно, но ведь об этом в условии задачи ни слова, не так ли?
Здравствуйте, Krovosos, Вы писали:
K>Трудно ответить на вопрос, которые выдает с головой ваше непонимание смысла ООП, не в обиду будет сказано. Но я мужественно попробую
Ответ на вопрос, которого не было, выдает с головой неясность и запутанность мышления. В моем письме не было вопроса
По существу, иерархия — это не увеличение сложности. Иерархия это система отношений между типами. Отношения между типами нужны, потому что типы бывают взаимозаменяемыми. Иерархия это один из способов реализации подтипов: везде где требуется базовый класс, можно подставить дочерний. В довесок, ОО-языки совмещают это с наследованием реализации, хотя и не всегда, что имеет свои проблемы, некоторые из которых показаны.
K>>Трудно ответить на вопрос, которые выдает с головой ваше непонимание смысла ООП, не в обиду будет сказано. Но я мужественно попробую
Кё>Ответ на вопрос, которого не было, выдает с головой неясность и запутанность мышления. В моем письме не было вопроса
Значит мне показалось
Кё>По существу, иерархия — это не увеличение сложности.
Увеличение. Я же говорю возьмите три независимых класса и три класса, которые наследуют друг от друга. Где будет больше неопределенности в поведении?
Кё>Иерархия это система отношений между типами. Отношения между типами нужны, потому что типы бывают взаимозаменяемыми.
"Взаимозаеменямость" это всего лишь реализация интерфейсов. Иерархия (суть — наследование поведения) отсюда напрямую никак не вытекает.
Кё>Иерархия это один из способов реализации подтипов: везде где требуется базовый класс, можно подставить дочерний. В довесок, ОО-языки совмещают это с наследованием реализации, хотя и не всегда, что имеет свои проблемы, некоторые из которых показаны.
Еще раз говорю: определитесь с целью. Зачем именно нужно Вам подставлять дочерний вместо базового? Почему нельзя обойтись интерфейсами, зачем именно нужен общий базовый класс?
Это иллюзия, что хорошая продуманная иерархия классов придает стройность и упрощает проекты. Все как раз наоборот.
Чем идеальнее Вы развяжете классы и отсечете их друга от друга — тем проще будет жить.