Comments 36
А то, что полноценное наследование — большой геморрой, стало понятно после появления C++: мало того, что понимание работы класса требует изучить весь граф предков, так ещё и внесение изменений в один из классов-предков может самым неожиданным образом сломать работу потомков — код становится крайне хрупким.
Именно потому в последующих ООП-языках наследование было предельно кастрировано (до линейного списка предков, что сгладило, но никак не решило проблемы), а основой полиморфизма стали интерфейсы, не создающие вышеназванных проблем.
Единственная реальная причина использования наследования в современных ООП-языках — уменьшение дублирования кода. Но и это можно решить без наследования: например, через типажи (trait) и обобщённое программирование (generic).
P.S. Для моделирования многокомпонентных систем лучше всего подходит парадигма компонентно-ориентированного программирования. ООП-же лишь имитирует модульность.
ООП через композицию обеспечивает такую же мощность кода, но без геморроя, вызванного наследованием.Сравнение кастрюли со сковородкой. Композиция и наследование — это РАЗНЫЕ инструменты для решения РАЗНЫХ задач. Если вам по предметной области нужно именно наследование, то решение этой же задачи через композицию, как правило, приводит к огромным объёмам boilerplate-кода, в котором неизбежно будут стадами пастись баги. И в результате код будет более «правильным» но менее рабочим.
А будет там бойлерплейт или нет — зависит от языка. На плюсах будет, да.
Но даже на них я предпочту избыточность неопределенности.
Задача ровно та же самая, переиспользование кода.
Нет! Наследование не для этого, оно нужно для бранчинга. Я искренне не понимаю, откуда вообще взялось это Композиция против Наследования, это вообще разные вещи. Переиспользование и модульность — это к композиции, а абстракции и бранчинг — наследование, вообще НЕ пересекающиеся вещи.
Если вам по предметной области нужно именно наследование, то решение этой же задачи через композицию, как правило, приводит к огромным объёмам boilerplate-кода, в котором неизбежно будут стадами пастись баги.
Если этот бойлерплейт генерируется, скажем, макросами, то не будут.
Наследование нужно для моделирования отношений "является" между объектами и классами.
Вы не поверите, но в «начале» в ООП вообще не было никаких классов. Классы появились после появления отдельных реализаций парадигмы.
И в этих реализациях наследование описывает иерархию классов и только. Причём здесь отношения между классами и объектами?
Это можно сделать с помощью интерфейсов или трейтов. Более того, так можно делать несколько отношений "является" без типичных проблем множественного наследования.
Можно, кто же спорит, но как по мне код типа
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 проверок? Вот отсюда и растут ноги у всяких странных функций выведенных в базовые объекты. Естественно, такое лепить всюду нельзя, надо думать в первую очередь о последствиях, но иногда можно нагрязнить.
А для моделирования лучше всего подходит ООП парадигма
А вот тут любители функциональщины не согласятся :)
a = F/m — моделирование нерелятивистского движения тела в инерциальных системах отсчёта )
ООП — это плохо. У этого человека много качественных видео, где это детально объяснено с примерами.
https://youtu.be/QM1iUe6IofM
Некоторые люди считают ООП неудавшимся экспериментом, который потихоньку заканчивается. Не зря в современных языках (Rust) ООП уже не поддерживается.
Из минусов — возможно может быть не комфортно общаться на собеседованиях
И как выявить тот факт, что человек — это животное и блох у него не может быть в принципе?
Мы про конкретную модель
oHuman instanceof oAnimal или типа того есть во всех ООП языках, что я встречал, а fleas нет ни в oHuman, ни в oAnimal
Кроме того выяснять наличие блох с помощью instanceof — это прямое нарушение LSP.
Если моделирование бизнес области требует, можно заменить блох на котейнер «Паразиты», где блохи один из возможных вариантов.
В реальном мире они может и есть, но в нашей модели их нет, и "нет" значит быть не может, а не нулевое количество.
В чём нарушение? Компилятор или что там у нас вместо него не даст вызвать thing.fleas на инстансах, объявленных как oAnimal или oHuman.
Зачем нужно понимать ООП