Как стать автором
Обновить

Комментарии 36

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

А то, что полноценное наследование — большой геморрой, стало понятно после появления C++: мало того, что понимание работы класса требует изучить весь граф предков, так ещё и внесение изменений в один из классов-предков может самым неожиданным образом сломать работу потомков — код становится крайне хрупким.

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

Единственная реальная причина использования наследования в современных ООП-языках — уменьшение дублирования кода. Но и это можно решить без наследования: например, через типажи (trait) и обобщённое программирование (generic).

P.S. Для моделирования многокомпонентных систем лучше всего подходит парадигма компонентно-ориентированного программирования. ООП-же лишь имитирует модульность.
ООП через композицию обеспечивает такую же мощность кода, но без геморроя, вызванного наследованием.
Сравнение кастрюли со сковородкой. Композиция и наследование — это РАЗНЫЕ инструменты для решения РАЗНЫХ задач. Если вам по предметной области нужно именно наследование, то решение этой же задачи через композицию, как правило, приводит к огромным объёмам boilerplate-кода, в котором неизбежно будут стадами пастись баги. И в результате код будет более «правильным» но менее рабочим.
Ну почему же. Задача ровно та же самая, переиспользование кода.
А будет там бойлерплейт или нет — зависит от языка. На плюсах будет, да.
Но даже на них я предпочту избыточность неопределенности.
Задача ровно та же самая, переиспользование кода.

Нет! Наследование не для этого, оно нужно для бранчинга. Я искренне не понимаю, откуда вообще взялось это Композиция против Наследования, это вообще разные вещи. Переиспользование и модульность — это к композиции, а абстракции и бранчинг — наследование, вообще НЕ пересекающиеся вещи.

Если вам по предметной области нужно именно наследование, то решение этой же задачи через композицию, как правило, приводит к огромным объёмам boilerplate-кода, в котором неизбежно будут стадами пастись баги.

Если этот бойлерплейт генерируется, скажем, макросами, то не будут.

Я правильно понимаю — вы предлагаете использовать макросы для генерации бойлерплейт-кода, чтобы эмулировать наследование, лишь бы не использовать настоящее наследование?

Наследование нужно для моделирования отношений "является" между объектами и классами.

Зря вы так.

Вы не поверите, но в «начале» в ООП вообще не было никаких классов. Классы появились после появления отдельных реализаций парадигмы.

И в этих реализациях наследование описывает иерархию классов и только. Причём здесь отношения между классами и объектами?
Отношения играют роль, потому что как показывает моя практика (не мировая статистика, но все же), если связать наследованием классы только по общему поведению, то потом непременно в дочернем классе какой-то метод родителя делает не то что нужно и его нужно переопределять полностью, либо глушить (а тут уже SOLID нарушился), кое кто выделяет базовые классы, но такая иерархия однобокая и создана она лишь для удовлетворения нужд одного дочернего класса. Это потом приведет к проблеме, если появится еще один ребенок (а он появится, поверьте). Но тут есть проблема пострашнее, если изменятся безнес требования, и их нужно будет внести в базовый класс, вот тут вы и поймете что жизнь — боль, особенно если дочерних классов уже много. Вобщем и отношение «является» не всегда серебряная пуля, я бы тут наследовался если бы понимал что оно «является» и класс по моей бизнес модели уточняющий. Иначе я бы предпочел композицию.

В каком "начале"?


Object subclass: #Account.

Это уже не начало?

Это можно сделать с помощью интерфейсов или трейтов. Более того, так можно делать несколько отношений "является" без типичных проблем множественного наследования.

Можно, кто же спорит, но как по мне код типа


class User {
}

class Admin extends User {
}

более читабельный и поддерживаемый в большинстве случаев чем


interface User {
}

interface Admin extends User {
}

class UserImplementation  implements User {
}

class AdminImplementation implements Admin {
}

Зато вы можете иметь несколько независимых интерфейсов с методами по-умолчанию или трейтов, которые реализуют поведение, и имплементировать их одновременно. И при этом быть уверенными, что с множественным наследованием проблем не будет — компилятор не позволит

Это язык такие фичи должен поддерживать. Не во всех даже простые интерфейсы есть, а если есть, то, например, поддерживают только методы

вытекает с из наследования

регистратору с из другого отдела


Здеь следует использовать предлог «из» вместо «с».

Там половина текста состоит из ошибок с ться/тся. Уже после половины статьи не мог всерьёз её воспринимать. Ощущение такое, как будто писал или студент для курсовой или вчерашний студент

"Само по себе" не работает. Инкапсуляция — это именно адекватное объединение данных и кода, с ними рабоащего в один объект. Низкая связанность, высокая связность (не перепутал?) — вот это вот всё

Оффтоп.


Опять 25. Ещë одна статья, в которой автор решил, что все вокруг непоняли ооп и счëл долгом объяснит всë решительно последний раз.


Алана Кея упямянули хоть?

Наш мозг воспринимает мир как набор объектов, которые взаимодействуют друг с другом.

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

Если функция — просто действие без контекста, то метод класса это уже действие в определенном контексте, это действие, которое относится к определенному объекту.

В функциональном программировании функция действует в контексте, просто этот контекст вся программа (или модуль).

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

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

Представляю эту картину в реальной жизни, у нас есть регистратура, где некий администратор ведет записи о посетителях в тетрадку. Также в этой тетради можно читать данные о посетителях. Мы получаем посетителя — Model, человека в регистратуре — Controller и тетрадь View. И в вышеуказанной интерпретации принципа наследования мы получаем, что посетитель всегда является регистратором и тетрадкой. Уже дико выглядит то, что человек и тетрадка одно целое.

Да, ООП это про моделирование. Но оно не про моделирование реального мира. ООП про моделирование необходимой информации. И если для задачи удобно, что бы тетрадка и человек были одни объектом, то можно их смело объединять.

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

Дело в том, что моделирование вообще в принципе не может полностью описать реальность. И иногда для того, что бы необходимый кусок реальности описать, приходится отходить от принципов хорошего программирования (солид и прочее). Что будет эффективнее (с точки зрения затрат), добавить 10 классов и 20 функций, но сохранить красивую картинку наследования или одно поле и 5 проверок? Вот отсюда и растут ноги у всяких странных функций выведенных в базовые объекты. Естественно, такое лепить всюду нельзя, надо думать в первую очередь о последствиях, но иногда можно нагрязнить.

А для моделирования лучше всего подходит ООП парадигма

А вот тут любители функциональщины не согласятся :)
А для моделирования лучше всего подходит ООП парадигма

А вот тут любители функциональщины не согласятся :)

Можно пример ФП моделирования?
В данном случае моделированием можно считать структурирование информации. И ФП этим вполне занимается в форме кортежей, функций и прочим.
На самом деле там более обширный инструментарий. ФП языки, как правило, имеют хорошие возможности для создания DSL, которые в дальнейшем и используются для описания предметной области. Плюс позволяют гораздо более точно управлять контекстом исполнения, предоставляя гарантии там, где при императивно-ОО подходе приходится довольствоваться соглашениями.

a = F/m — моделирование нерелятивистского движения тела в инерциальных системах отсчёта )

ООП — это плохо. У этого человека много качественных видео, где это детально объяснено с примерами.
https://youtu.be/QM1iUe6IofM


Некоторые люди считают ООП неудавшимся экспериментом, который потихоньку заканчивается. Не зря в современных языках (Rust) ООП уже не поддерживается.

Забавно выглядит "в современных языкАХ" и только один пример, Go бы добавили сразу )

Go

Мамонт со сборкой мусора

Сборка мусора в лиспе была. Это 60-е, кажется.
А вот CSP — уже конец 70-х.
Неплохая статья с еще одной попыткой обьяснить принципы ООП на примере iOS разработки) Повторение мать учения)
Выскажу непопулярное мнение на счет ООП, может кого-нибудь заинтересует. Надо стараться воздерживаться от активного использования терминологии ООП (неважно какого именно языка, рантайма исполнения, стиля). Например любую задачу рассматривать оперируя только интерфейсами сущностей (ответственностью) и реализациями этих интерфейсов. Некоторые плюсы такого подхода — вы не зависите от языка программирования и стиля на текущем проекте, можете говорить на одном языке с аналитиком.
Из минусов — возможно может быть не комфортно общаться на собеседованиях
Забавно, но на картинке где «ООП, которое пользователи заявляют» идеальный пример как не стоит моделировать. Вместо иерархии классов достаточно иметь один класс где ноги, тип животное/человек, блохи — это всего лишь аттрибуты.

И как выявить тот факт, что человек — это животное и блох у него не может быть в принципе?

В принципе могут . А как имея базовый класс oAnimal выяснить этот же факт? В простом дизайне по дефолту fleas = 0 и это уже зависит от контекста каким образом это значение может/не может измениться.

Мы про конкретную модель


oHuman instanceof oAnimal или типа того есть во всех ООП языках, что я встречал, а fleas нет ни в oHuman, ни в oAnimal

И тут выясняется, что у людей они таки есть и что возможно кроме oPet, могут быть другие наследники oAnimal (oWild) у которых тоже могут быть блохи.

Кроме того выяснять наличие блох с помощью instanceof — это прямое нарушение LSP.

Если моделирование бизнес области требует, можно заменить блох на котейнер «Паразиты», где блохи один из возможных вариантов.

В реальном мире они может и есть, но в нашей модели их нет, и "нет" значит быть не может, а не нулевое количество.


В чём нарушение? Компилятор или что там у нас вместо него не даст вызвать thing.fleas на инстансах, объявленных как oAnimal или oHuman.

но в нашей модели их нет, и «нет» значит быть не может

Сегодня нет, завтра есть. Про то и речь, что предложенная модель плохо подходит для расширения функуционала.

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.