Комментарии 62
ООП создавалось для управления сложностью, разбиения программы на переиспользуемые «кирпичики». Объект в ООП — это набор операций над неким общим состоянием.
Построение абстракций ООП на основе сущностей реального мира — одно из самых частых заблуждений в ООП
Почему это − заблуждение, кто ещё приходил к этому выводу, что можно почитать на эту тему?
Я, например, ни разу не видел учебника, который не использовал бы метафор из реального мира, чтобы объяснить принципы ООП. Википедия говорит о “things found in the real world”. В общем, если это и заблуждение, то оно слишком распространено, чтобы отметать его так запросто, вы не думаете?
Построение абстракций ООП на основе сущностей реального мираСущности реального мира и позволяют строить ооп-абстракции. Более того, ооп — это всего лишь подмножество какой-либо логики высшего порядка, а точнее — реализация логики. Но сама логика ооп из ниоткуда не берется же. Кто придумал и исходя из чего полезла логика ооп? Неужели с неба упала?)) Или полезла из математики что ли?
И он действительно не понимает следующего, на пальцах оно вот так:
Есть множество X и Y. И есть элемент, принадлежащий множествам X и Y одновременно. Как формализовать сие? Самое простое решение, прямолинейное и тупое «на кванторах и предикатах» — это два равноправных множества X и Y, включающие элемент А оба, и тут говорят конечно же, что эти множества X и Y пересекаются элементом А. Но какому-то чудаку пришло в голову следующее. А что если организовать все это дело по-другому? Если взять Y и сделать подмножеством X, да еще и так, что бы А попадало в оба множества, то получается же совсем другая логика) Элемент А принадлежит Y, но Y — подмножество X, и тогда и получается решение, но другое. Пытливый читатель сразу же увидит тут то, что X и Y можно поменять местами, т.е. X сделать подмножеством Y. А это уже третье решение)
ТАК КАКАЯ ФОРМАЛИЗАЦИЯ ВЕРНА?
Математика на этот счет вообще не имеет ни теорий, ни даже подступиться не может. Впрочем математика оным и не занимается. А решение тут одно — выбирайте сами что вам удобнее, и это и будет ваша логика. И логик таких, при условии огромного кол-ва элементов и связей между ними — так же преогромное кол-во))
И это всего лишь вершинка айсберга. Маленькая снежинка на макушке. На самом деле все настолько грандиозно, что несведущие «читанув» ооп, и совершенно не поняв откуда что лезет, например то же ооп — и плетут в общем-то всякую фигню… Не обращайте внимания, это нормально. А вы же задали вполне корректный вопрос, по поводу ооп и реального мира. Ответ тут прост. ООП — это всего лишь один из бесконечного кол-ва способов описания реального мира. На самом деле ответ вот такой: ООП — это один из способов реализации подмножества логики высшего порядка.
Я, например, ни разу не видел учебника, который не использовал бы метафор из реального мира, чтобы объяснить принципы ООП
Я например в таком виде совсем не воспринимаю ООП (возникают вопросы: почему реализуется так, а не иначе). Мне ближе «железный» подход: формы, кнопки, реализация в машинном коде.
Т.е. мы здесь описываем сущности предметной области как пассивные (не имеющие поведения) наследуемые объекты и снабжаем их функциями, которые позволяют проще получать информацию об этих объектах.
С другой стороны, не представляю ситуации, в которой эти объекты имело бы смысл снабжать некими функциями, определяющими их поведение. Потому что поведение в разных задачах может быть очень разным и потому что поведение системы сразу станет не предсказуемым и сложным. Это как заставить клеточный автомат сделать нечто задавая правила его работы.
Для меня в этом случае проще реализовать все требуемые алгоритмы в нескольких крупных объектах, каждый из которых умеет решать некую задачу поверх описанной выше предметной модели объектной области и инкапсулирует требуемые попутно данные. Когда постоянно существующего состояния почти нет, тогда вообще проще — написать все это опираясь на функциональный подход где это просто и скатываясь к процедурному, где с чистыми функциями все слишком сложно.
Т.е. мы здесь описываем сущности предметной области как пассивные (не имеющие поведения) наследуемые объекты
Тогда вы не используете ООП, оно о данных инкапсулированных с поведением.
С другой стороны, не представляю ситуации, в которой эти объекты имело бы смысл снабжать некими функциями, определяющими их поведение.
Ну как же?! А передаточные функции?
ООП о данных инкапсулированных с поведением
Антисолид. Данные от логики управления ими лучше отделять.
А вот GRASP, например, прямо совсем с вами не согласен.
Ответственность должна быть назначена тому, кто владеет максимумом необходимой информации для исполнения
Принцип единственной ответственности (The Single Responsibility Principle)
Каждый класс выполняет лишь одну задачу.
Т.е. должна быть сущность, хранящее состояние и сервис, который ею манипулирует.
Таким образом, с сильной натяжкой, можно рассуждать лишь от DTO, да и то это скорее искусственный конструкт нежели полноценный объект в понятиях ООП.
Для меня это не работает.
Во-первых, скорее всего это будет не сам доменный объект, а некое его расширение, заточенное под конкретный способ моделирования (физически существующие объекты можно моделировать множеством способов).
Во-вторых, в общем случае моделируется система, состоящая из множества доменных объектов в комплексе и моделирование не сводится просто к имитации поведения объектов (могут быть настройки, несколько итераций и т.д.).
Поэтому удобно извлечь информацию из доменных объектов, преобразовать ее в более абстрактный вид (матрицы, векторы), и обрабатывать воздействие алгоритмом, который получает на входе абстрактное описание всей системы, заточенное под конкретный алгоритм.
Бывают ситуации, когда доменный объект — это не настоящий физический объект а нечто витруальное — намерение кого-то сделать что-то, акт получения информации и др. С такими объектами я сталкивался реже, но по-моему все-равно проще написать алгоритм, который получая на входе доменную модель и воздействие возвращает новую доменную модель, чем упаковать то же самое в алгоритмы внутри доменных классов.
В общем, кто знает другие примеры из реальной жизни, где в доменные объекты действительно удобно инкапсулировать поведение, интересно было бы узнать.
Кстати, выше сказанное не относится к объектам в играх или UI. С ними ООП работает получше.
— идеального проводника: что на входе, то и на выходе
— резистора: напряжение на выходе ниже напряжения на входе
— конденсатора: сдвиг по фазе
— индуктивности: сдвиг по фазе в другую сторону
В общем же случае, если у вас в объекте только данные (ну или данные и тупые гетеры/сеттеры), то это и не объект, а просто структура данных (struct в C, record в Pascal и т. д.)
Передаточные функции определены для четырехполюсников, а не для индивидуальных элементов.
Наследование у вас странное. Не может выключатель наследоваться от проводника, это будет нарушать LSP.
По поводу поведения — тут и правда сложно, расчет схемы — это решение системы уравнений, а систему уравнений на уровне одного объекта не решить. Однако, задачи построения этой системы или задача визуализации схемы вполне решаются методами ООП.
Наследование у вас странное. Не может выключатель наследоваться от проводника, это будет нарушать LSP.Это не я придумал и это даже стандарт. Они там наверно много сломали копий. Пусть это будет не проводник, а некий элемент оборудования, способный нести нагрузку в электрической системе. В любом случае, пока эти объекты доменной модели используются только для того чтобы описывать электрическую сеть как она есть, никаких проблем не возникает (хотя это еще не совсем ООП в его классическом понимании).
Однако, задачи построения этой системы или задача визуализации схемы вполне решаются методами ООП.Что касается визуального представление объекта электрической сети, то да, там получается почки классический ООП, хотя это уже не доменные объекты. Что касается построения, не знаю. Когда делал графические редакторы для моделей мне было проще выносить поведение в отдельные объекты-контроллеры, по одному контроллеру на каждый аспект поведения класса объектов. Как можно изящно запихнуть поведение прямо в объекты не представляю.
Это не я придумал и это даже стандарт. Они там наверно много сломали копий. Пусть это будет не проводник, а некий элемент оборудования, способный нести нагрузку в электрической системе. В любом случае, пока эти объекты доменной модели используются только для того чтобы описывать электрическую сеть как она есть, никаких проблем не возникает (хотя это еще не совсем ООП в его классическом понимании).Конечно родительская абстракция у выключателя — это элемент оборудования. Это же логично) А знаете почему? Потому что например интерфейсы этих абстракций будут логичными. Если вы абстракции «завяжете» не логично — у вас интерфейсы логикой потекут сразу же. Что есть абстракция? Это абстракция с абстрактной абстракцией [что называют интерфейсом].
Наследование у вас странное. Не может выключатель наследоваться от проводника, это будет нарушать LSP.Конечно странное. Имеем:
Проводник: ИнтерфейсАбстракцииПереключения-able
Выключатель: Проводник
Имеем явную логическую бредятину)
Должно быть вот так:
ЭлементОборудования: ИнтерфейсАбстракцииПереключения-able
Выключатель: ЭлементОборудования
В первом случае все потекло, во втором уже лучше. Заметьте, реализацией вообще никто не занимается.
Суть: На первом этапе проектирования абстракций постройте абстракции так, что бы они максимально отражали реальность.
Суть2: Наступает сильное понимание того, что абстракции в электрике чрезвычайно не прибраны логикой) И да, в итоге реальных абстракций может не хватить. Это одна из главных проблем ооп.
А теперь нужны электрики, которые внятно достроят дерево электрических абстракций полностью) Зачем оно нужно? Что бы не париться программистам. Тем более что программисты строя дерево абстракций за электрика — явно занимаются не своей работой… Понимаете масштаб проблемы?
пс: Если вы не можете на бумашке построить дерево абстракций той системы которую вы описываете — даже и не думайте ее программировать) Смысл же отсутствует тогда совершенно. Вы что программировать собрались-то?
Следствие: Если вы на бумашке не можете собрать логику абстракций, то любое безумное программирование такой логики заранее обречено на провал.
Не так, Нужно строить абстракции так, чтобы они максимально отражали важные для нашей задачи аспекты реальности. Для задачи учёта электрооборудования вообще может быть не важно Переключения-able оно или нет. :), а для моделирования переходных процессов в цепи — неважно, что золото на порядке дороже аллюминия.
А возникает скорее не потребность, а соблазн. :)
важные для нашей задачи аспекты реальностиРеальность — это реальность. В реальности или абстракции есть — или их нет. И выходит так, что если вы описываете абстракции реальности — то вы их описываете, и они у вас описаны. Другого не будет. Другое — это от лукавого. Всякая непонятная «важность» и т.д.
Ваша мысль про важность приводит еще и к следующему. Недоописав максимально полное дерево абстракций — вы получите не полную логику абстракций. Надеетесь на везение? Ну надейтесь…
Попробуем дописать то что выше
ЭлементОборудования: ИнтерфейсАбстракцииПереключения-able
Выключатель: ЭлементОборудования
И допишем вот так:
Проводник: ИнтерфейсАбстракцииПереключенияПроводника-ableПри этом, абстракция проводника должна быть в собственной логической либе, абстракция элемента обрудования в другой — это например позволит построить принципиальную схему проводников и их переключений, без гориллы и африки впридачу. Перепишем:
ЭлементОборудования: Проводник, ИнтерфейсАбстракцииПереключенияОборудования-able
Выключатель: ЭлементОборудования
Либа1.Проводник: ИнтерфейсАбстракцииПереключенияПроводника-ableИ т.д.
Либа2.ЭлементОборудования: Проводник, ИнтерфейсАбстракцииПереключенияОборудования-able
Либа2.Выключатель: ЭлементОборудования
Естественно не нужно оное воспринимать как готовое) Ибо я над этим думал аж целых 30 секунд… Такие вещи как архитектура абстракций разрабатываются чуть ли не месяцами, а иногда и годами. Берите и дописывайте оное. И проверяйте на логику. Чем больше допишете — тем меньше логических дыр вы получите в будущем. Максимальное из возможных описаний системы приведет к полному отсутствию логических дыр. При этом, т.к. логика абстракций позволяет выпилить ненужное абсолютно безболезненно, не участвующее в логике ПО, то выпилите оное) Или оставьте пустым, потом допишете реализацию «выпиленных» абстракций.
Пример переписанного выше позволяет это. Несколько абстракций я могу точно оставить недописанными. Но на общую логику оно не повлияет никак, и в будущем может быть дописано. Недописанное повлияет на реализацию, но не на логику. И это самое главное. Смысл ооп в этом и заключается. Напишите так, что бы ваши абстракции были максимально логичными. Если вы напишете по-другому — ну так ваши абстракции развалятся, и получите вы в итоге что-то другое, но не ооп.
А вы не ограничиваете? Классическую квантовую механику применяете или теорию струн? Или всё же, субатомные взаимодействия вам не важны и вы ограничивается атомарным, а то и молекулярным уровнем?
А вы не ограничиваете?Нет) Еще раз написать? Хорошо, напишу. На абстракции — абстракция важности не распространяется никак. С информационной точки зрения — нет) А если и распространяется — то абстракция важности должна быть прикручена. Другого нет, не было и не будет.
Квантовые вещи тоже можно прикрутить, абстрактно на бумашке. Почему нет-то? Другое дело что это вряд ли возможно, т.к. физические абстракции мне не доводилось сводить в кучу. Но если потребуется — я и физиков из-под земли достану, раздам задачи и в итоге получу то что нужно.
Понимаете масштаб проблемы?Да, и наблюдаю как эта проблема решается с 1995. Работает это примерно так. Электрики, которые немного программисты, описывают модель как она есть, чтобы ее можно было как-то вменяемо представить дата-инженерам предприятий и стараясь далеко не уходить от UML-ок, нарисованных в стандартах International Electrotechnical Commission. Потом программисты, которые немного электрики устраняют в модели явные косяки. Потом программист, который решает очередную прикладную задачу формирует по этой модели некие структуры данных, которые ему нужны для решения этой задачи. Или в простом случае работает прямо по модели. Или чаще — работает по своим структурам, обращаясь к модели для получения каких-то деталей. В обоих случаях доменная модель — это по сути статичное описание электрической сети с непосредственным доступом к каждому атрибуту без лишних абстракций.
Стандарт описывает онтологию электросхем, то есть набор терминов, а не иерархию классов. Когда вы выстраиваете иерархию классов в соответствии со стандартом онтологии, вы как раз и попадаете в описанную выше ловушку:
Построение абстракций ООП на основе сущностей реального мира — одно из самых частых заблуждений в ООП
Но бывают области, где абстракций особо не выделишьБрехня. Значит никто не стремится их выделять. Классификацию Линнея кто придумал до Линнея?)) Вот ровоно так же и с 1с — что бы появилось ооп — нужно придумать абстракции пригодные для логики ооп, в рамках решаемых задач 1с, но т.к. в 1с нет и не было вменяемых архитекторов абстракций — задача проектирования абстракций и не была реализована, а впрочем она даже и не ставилась.
В современном мире с большим количеством средних программистов объектно ориентированный код с фабриками фабрик фабрик фабрик не менее write-only, чем код на Perl.
«Cпособ мышления — Форт язык и философия для решения задач» (автор Броуди) -1984г
Все утверждения группы [1] и [2] транслируются в ООП, для которого термин компонент заменяется понятием класс.
Вот и вы путаете класс и объект.
Объектно ориентируемое программирование создавалось для моделирования объектов.
Классы же хоть и были изначально, однако как удобство, деталь реализации. Но зачем читать что пишет автор. Давайте посмотрим на реализацию, и так все понятно.
Вообще про полиморфизм мне в своё время понравилось обсуждение на RSDN, копия поста доступна здесь — gaperton.livejournal.com/15121.html
Он сделал это в классическом для java стиле ООП и утверждал, что у него там все очень хорошо продумано и при внесении изменений достаточно будет просто заменить несколько классов при инициализации. И ему это действительно удавалось.
Позже мне захотелось разобраться как работает этот модуль. Я залез в код и увидел кучу фабрик, визиторов, интерфейсов и прочего. Очень быстро я почувствовал, что сложность всего этого на порядок превосходит возможности моего мозга и бросил эту затею, решив что работа с чужим кодом мне не по зубам.
Позже в другой компании я снова столкнулся с кодом других программистов. Этот код был почти процедурным с очень крупными объектами. И здесь картина была противоположной: через 5 минут просмотра кода я уже понимал что здесь нужно добавить и что переделать. Дальше я менял код, и если это был не мой компонент, просил у ответственного сделать ревью моих изменений.
До сих пор не могут понять что такое этот суровый java-ООП с патернами и SOLID — может это коллективное заблуждение, может это попытка установить любые правила разработки (какие-то лучше никаких), может это фильтр, позволяющий не подпускать к проекту недостаточно одаренных и опытных, может я не вижу какого-то очень важного фактора…
Как использовать здесь больше объектного программирования и не утонуть в трудностях для меня не ясно.
class Animal
class Cat(Animal)
Animal a1 = new Cat();
Animal a2 = copy of a1;
Казалось бы что может быть проще? Но тут начинается трип в махровый мир ООП. Каких только решений тут не встречал. От нудной реализации Clonable во всех субклассах, до развесистой рефлексии и хитрой сериализации-десериализации.
А при чем тут ООП вообще? Это особенности конкретного языка программирования.
Как будто на обычном С обычные структуры с вложенными указателями на другие структуры было бы проще склонировать.
$a1 = new Cat();
$a2 = clone $a1;
:)
Два варианта.
Первый — вам действительно тогда не хватило мозгов, второй — там было сделано сложнее, чем оно того требует.
Проверить довольно просто. Если в "ООП"-варианте, понимая архитектуру системы, изменений вносить требуется существенно меньше, чем в "полупроцедурном" — значит, там все было сделано хорошо. Если примерно одинаково — значит это был оверинжиниринг.
Новички в ООП часто прямо описывают все поведение в объекте доменной области и получают god object, который невозможно переиспользовать и сложно сопровождать.
Ещё одна проблема возникает при включении объекта в окружение. В этом случае часто возникает неопределенность ответственности при описании взаимодействия двух и более объектов. Например, при столкновении двух объектов типа «мяч», у какого из объектов вызвать метод, мяч1 ударить мяч2 или мяч2 ударить мяч1?
Правильное ООП, вместо инкапсуляции наследования полиморфизмов (вы что издеваетесь?), должно заставлять разработчиков производить сразу всем понятные проектные метрики: сроки, прибыль, зряплату и все такое. ;)
если их 4ре: abstraction, encapsulation, inheritance, and polymorphism
Эволюция программного проекта и ООП