Сергей, это крутой видос! Скажу по себе, иногда бывает застрянешь на каких-нибудь простых темах, и вот именно такие подробные объяснения расставляют все по полочкам в наших головешках)
Подытожу кратко, что сказал Сергей в видео: - "Применяйте наследование там где оно действительно нужно, если в нем нет смысла используйте делегирование. Больше думайте и применяйте все по назначению, а не потому, что так модно"
На моей практике , можно писать исключительно используя композицию . Как говорил Блох: наследование - сильная связь. А она попросту в большинстве случаев не уместна .Но это не значит , что наследование - зло. Все зависит от конкретной задачи, для этого и существует декомпозиция предметной области.
Почему не упоминают , что есть наследование только интерфейса, а есть наследование реализации. Первую никто вменяемый вроде особо и не критикует. А вторую советуют не применять.
@@МаксимВеснин-ш3р Есть имплементация - это когда Объект реализует какой то функционал, а есть наследование интерфейсов - это когда интерфейс наследует поведение другого интерфейса.
"полиморфизм тоже невозможен без наследования" расскажите кто-нибудь автору про шаблоны в c++, про трейты в rust... Дядька крепко застрял в модных концепциях ООП из начала 90-х.
Полезный ролик. Про нормализацию баз данных тоже нубам расскажите, чтоб не портились протоколы измерений при удалении типа двигателя в справочнике двигателей.
5:32 да именно так, в функциональной парадигме всё иммутабельно! Используем только структуры, никаких объектов. Если конечно не пишем на Scala, но и там стараемся от иммутабельности избавится
Пример с сотрудниками неудачен. Сотрудник - это сотрудник, а программист - это роль сотрудника. Таких ролей может быть много. Сотрудник может быть и программистом, и аналитиком одновременно. Т.е. программист - это не наследник сотрудника.
@@sardaucar это еще и человек, наследующийся от млекопитающего - ты уверен что от этого объекта на роли программиста требуются все методы, включая жрать(), срать(), спать()? от него требуются методы которые ему характерны и да, он может имплементировать множество интерфейсов, один из которых скажем сотрудник, с методами ходить_на_работу(), получать_зарплату(), участвовать_в_корпоративах(), а другой интерфейс - программист, с методами писать_код() дебажить(), задавать_глупые_вопросы_в_интернетиках(). то же самое и аналитик. но вот допустим нюанс - а если аналитик удаленный? тогда чо? будем ему перепиливать метод ходить_на_работу()? а если у нас 100500 удаленных ролей - всем им перепиливать? упс, да? :)) поэтому наследование - ну такое. тут просто Сергей застрял немного во времени и сейчас в современных языках уже появились возможности безболезненно пилить реализации для интерфейсов.
Ассоциация, агрегация и композиция ни чем не хуже, наследования, они просто для разного. Но почему-то наследование вынесено в основной принцип, а они - нет. Как только новичок выучивает наследование, то у него сразу замдиректора наследует от директора, потому, что он его старший сын, а метод начисления зарплаты находится у уборщицы, а не у бухгалтерии.
Это вопрос уже "Насколько развито абстрактное мышление". Многие люди не умеют выделять из объектов общие свойства и создавать родителя с этими свойствами. Есть директор, есть зам. больше ничего не надо, директор главнее, значит он родитель.
@@SergeyNemchinskiy Маразм проходит циклическое развитие, то мода на одно, то на другое. Это редукция сложного к одному, она конечно нужна, но первые 10 лет выходит что выходит))) ну и это смотря в каком языке еще, разные языки на разной стадии моды в данный момент
В целом согласен с вами. Хочу ли добавить пару моментов. Возможно стоит разделять наследование на наследование и реализацию. Добрую часть паттернов можно организовать используя реализацию (от интерфейсов или абстрактных классов). Стоит также держать в голове мысль, что наследованием нужно пользовать с осторожностью, так как это самый жесткий вид взаимосвязи между классами. Не подумайте, это не хейт наследования, это попытка сказать, что нужно учиться проектировать программы в ООП стиле)
Сергей! От души рад, что позволили подписаться на Вас, влекущее автоматическое уведомление на смартфон и позволяющее на ровном месте ЛикБез-сть! и по-брацки.
Наверно это самая важная часть в книге банды четырех: специфицирование интерфейсов объектов и далее (стр 27, docs.google.com/file/d/0B6GuCegBf3X3Tm1rZl9BUTduQm8/edit). Они четко разделяют понятия интерфейс/тип (причем это не только то, что находится после слова interface) и реализацию/класс. "... интерфейсы ... фундаментальны". Только после разделения этих понятий в голове становятся возможны: декораторы, адаптеры, композиции и др. Если же рассматривать их в одной куче, то получаются что без наследования (в целом) невозможно строить полиморфизм (большинство паттернов его частный случай). На самом деле его невозможно строить без разделения между несколькими классами интерфейсов или попросту наследования классами интерфейсов.Пример в видео с зарплатой приведен не очень корректно. Обычно этот расчет занимает сотни и тысячи строк кода и обычно его хочется реализовывать где-то еще, а не в сотруднике. Наследование интерфейсов позволит реализовать его во вне понятия сотрудник, а наследование реализаций заставит вас сделать божественный базовый класс. Отсюда, на мой взгляг, и проблема наследования реализаций, но к наследованию интерфейсов это не имеет ни какого отношения.
Без наследования в принципе нельзя писать приложения под тот же Android, например, где все элементы UI наследуются от View. Хочешь запилить кастомный контрол - наследуешься от View (или от любых других его потомков, не помеченных как final). Забавно, что в Android кнопки (Button) наследуются от TextView, и, если глянуть в исходники TextView и Button, можно увидеть, что в TextView около 13000 строк Java-кода, в то время как в Button не набирается и 200 (бóльшая часть из которых - javadoc).
На самом деле хрень редкая. На предприятиях где этот расчет зарплаты реально применяется запросто бывают ситуации когда сотрудник 10 дней работает токарем, 5 дней грузчиком на погрузке вагонов, 2 дня сварщиком и 3 дня белит потолки. Это обычная, рядовая ситуация. Успехов вам с вашим ООП.
@@TwilightSun32 для рынка постсовка в основном так и делают, но Немчинский занимается подготовкой спецов в первую очередь на проекты для западных заказчиков
Я всегда говорю, что надо зреть в корень и понимать, как что работает и почему. Нам ещё в институте приводили в пример такую историю. Женщина нарезала бекон маленькими полосками перед тем как обжарить его на сковородке. Её спросили зачем она это делает? Почему просто не жарить его так как он порезан в упаковке? Она сказала, что так делала её мама. А мама сказала, что так делала её мама. Когда у бабушки, спросили тоже самое, она сказала, что в её время сковородки были очень маленькие и длинные полоски просто не помещались. Это конечно всего лишь история, но суть передаёт. Если вы не понимаете зачем вы что-то делаете, возможно вы делаете это в пустую. Так как возможно причина этого уже не существует.
Ну вот с повышением уборщицы до программиста, если это разные классы, даже с общим предком, всё равно надо один объект убить, другой создать. Правда, ко мне, как к программисту PHP, это всё не относится :))) У меня объекты так долго не живут :)))
Когда Сергей сказал, что использование полиморфизма без использования наследования невозможно, где-то в тёмном уголке тихонечко заплакали интерфейсы... от смеха...
Если учитывать, что интерфейсы выросли из полностью абстрактных классов для решения проблемы множественного наследования, то это тоже в каком-то смысле наследование, пусть и названное в джаве как implements.
@@0imax А почему они "выросли"? Потому что у наследования есть проблемы. При этом отказываться от наследования полностью тоже считаю идиотизмом. Оно нужно там, где оно нужно. Главное - не возводить его на пьедестал.
Почему-то программисты, когда приводят пример другого сотрудника, чаще всего упоминают уборщицу. Видимо это тот сотрудник, который чаще всего отвлекает их от работы ... или прокрастинации.
Просто программисты, почему-то, привыкли и продолжают считать себя обслуживающим персоналом, наравне с уборщицей, типа бизнес приносит деньги, клерки и менеджеры главное звено... давно это уже не так. Снимайте тишорты, надевайте версаче. Дальше действовать будем мы (с) В. Цой
Пример с сотрудниками не корректен. По сотрудникам как раз стоит юзать композицию "сотрудник" + вложенный объект "функциональная спецификация" . И не забывать в "сотруднике" про атрибутивный состав (положение в штатке, расположение рабочего места и т.д.) Если же все отдельные классы сотрудников наследуются от базового, то изменение штатки - убийство существующего объекта и создание нового. Опять же, начальник отдела программистов может быть иле не быть программистом, но менеджером он быть обязан.
Например, у нас базовый класс сервиса, а остальные реализации мы передаем в качестве агрегированных объектов. И вот у нас без наследование точно так же всё работает)) Только удобнее менять) И можно проверять реализации и тестировать их не зависимо от базового класса. А базовый класс не зависимо от реализации так называемых наследников.
Создатель ООП Алан Кэй об ООП: "ООП для меня это сообщения, локальное удержание и защита, скрытие состояния и позднее связывание всего. Это можно сделать в Smalltalk и в LISP." Всё.... Остальное наворотили авторитетные люди. Поэтому, если кто-то говорит о проблемах наследования, то не стоит ссылаться на догмы о трёх принципах...
Только вот какая разница что там думал чел, язык которого не взлетел от слова совсем, а то ооп, которое в итоге позволило создать колоссальные приложения, не похоже ни на йоту на ооп его "создателя".
Есть программа, которая написана на фортране, моделирующая перенос фундаментальных частиц. Софт коммерческий, и широко используется для расчётов. Так вот там тысяч 50 строк кода, не меньше. Она в принципе поставляется с исходниками для возможной ее модификации. Чтобы ее модифицировать, надо перелопатить охрененную тучу кода!!! Зато фортран. С кусками Си. Возможность подключать определенные библиотеки физических величин.
наследование использую только в написании каких-то системных вещей или системных (не связанных с конкретной бизнес логикой) либ для приложения, где логика понятна и вряд ли поменяется. практика показала, что наследовать объекты реального мира, то есть брать данные из ТЗ и делать из этого какую-то иерархическую структуру как правило приводит в перспективе к говнокоду и усложнению поддержки, из-за того что бизнес меняется очень быстро. Сегодня программист - это сотрудник, завтра он уже "удаленный сотрудник", а еще вот есть "удаленный на полставки". В итоге сегодня мы под ТЗ делаем красивую стройную модель, а завтра малейшее изменение приводит к тому, что нам надо рефачить все дерево и все зависимые от либы проекты. Какой выход? под конкретные проекты по-разному придумываем, но часто стараемся делать так, чтобы переиспользование кода было минимальным как раз. Если у меня есть похожая логика, я ее не выношу в отдельный модуль или библиотеку, а прямо копипащу с небольшими изменениями. В итоге новый человек на проекте или я через год, когда нужно будет логику поменять, увидит, что определенный метод используется всего в 1 или 2 местах в коде и описывает небольшой кусочек (SRP привет), чем код, который растянут на 10 наследников и еще переопределен в куче мест. Не утверждаю, что наследование плохо, просто на практике оказалось выгоднее стараться обходиться без него, ну или скорее не совать его везде. П.С. у S0ERа есть хорошее видео на тему копипаста кода. согласен с ним, что копипаст, это не всегда зло. Если логика просто похожая, но логически другая - здесь копипаст это не дублирование кода, так как логически эти методы про разное и похожий код не является дублированием.
Скорее всего это означает что вместо введения свойств сотрудника, которые описывают способ работы и способ оплаты добавили классы наследники. Не нужно наследовать на каждый чих.
@@yuriy.kostenko ну об этом я и говорю, мы не описываем реальный мир наследованием. что касается такого подхода как введение свойств вместо наследования. он тоже достаточно дискуссионный. допустим у меня метод, который работает только с удаленными сотрудниками. Если у меня не будет соотв. класса под это, мне нужно в рантайме проверять на галочку "а передали ли мне удаленного сотрудника или же потребитель моего кода мне подсунул ненужные данные". при введении соотв. класса мы это проверяем на этапе компиляции - потребитель просто не засунет в наш метод ничего.
@@Dimonina В чем проблема вынести подобные алгоритмы работы в отдельные классы-стратрегии? Как раз-таки наследование и нужно для того, чтобы решить описанную тобой проблему. Выносишь поведения "удаленный сотрудник" и "сотрудник на полставки" в отдельные классы-стратегии и внедряешь зависимость в свой класс. Если в будущем надо будет добавить еще какой-то тип сотрудника, ты просто расширяешь свой код через наследование, а не изменяешь уже разработанный и протестированный, и не пишешь огромную пелену кастов и условных операторов для определения типа сотрудника. Если возникла необходимость комбинировать, например "удаленный сотрудник на полставки", то оборачиваешь это все в декоратор. Я не вижу тут никакой проблемы наследования, а только типичную ситуацию, в котором оно полезно
@@МаксимМалышев-м6ы по тексту сложновато понять конкретную реализацию, я бы в код глянул. концепт понятен, но вот реализация может быть разной. если будет время и желание, посмотрел бы пример. возможно говорим об одном и том же, просто с разных углов
@@Dimonina по моему мнению как раз именно наследованием мы описавыем реальный мир. И свойства обьектов в реальном мире очень даже проверяются. Типичный пример касса в магазине. Касса работает с людьми, а не с покупателями (потому что еще не факт что человек станет покупателем, вдруг его свойство "количество денег" не достаточно для того что он приволок на кассу) и проверяет много свойств, в том числе If(покупает алкоголь && свойство Возраст > 18) OK else() "а ну брысь!" Как-то вот так. )
Сергей, здравствуйте. Спасибо за видео. Одна просьба. Не могли бы вы выводить на экран название английских терминов, которые используете. Не все удается разобрать
Судя по моему опыту, могу сказать скорее вопрос о необходимости использовании ООП в определенных задачах. При драйверном уровне приложений ООП может быть через-чур избыточен, а как следствие большая часть парадигм просто не будет использоваться. С другой стороны есть множество задач где ООП является оптимальным вариантом использования. А еще появляется такая интересная особенность что раз язык позволяет использовать парадигму, то ее порой стремятся использовать просто потому что такой инструментарий есть. Выводы каждый прочитавший думаю сделает сам. Зачем наследование Именно в ооп - ответ дан автором видео.
Я вот рассматриваю Java, как будущее хобби и ещё не написал ни одной серьёзной программы в жизни. Но с каждым подобным роликом т.н. "порог вхождения" для меня становится существенно ниже. Спасибо.
Есть такой признак неиспользования полиморфизма и наследования: это наличие больших, разветвленных, иногда многовложенных switch/case или if/then/else .
Касательно заключительной части ролика, про то, что наследование следует использовать в тех местах, где оно имеется в реальном мире. Читал на хабре статью и соответственно слышал мнение, что само по себе мышление программиста реальными сущностями - бич. И нужно наоборот стараться мыслить структурами данных и абстрактными объектами, которыми в первую очередь удобно мыслить и действовать в данном случае. Интересно мнение.
Ого, я стал настолько старым, что начал узнавать что-то новое. Например, в этом ролике услышал, что появилась мода считать, что наследование не нужно ) А патерны, в теории, можно и без наследования делать. Полиморфизм можно сделать указателями на функции (делегатами). Ах, да, я ж сишарп программист. Но на джаве там тоже как-то это можно сделать, подобие. Наверное. Если в классе без наследования хранить поле - указатель на функцию, и его подменять, для разных объектов получая разное поведение, то получится и полиморфизм без наследования, и остальное можно постаравшись, поизвращавщись, получить. Не знаю зачем. Может кто педантично ненавидит наследование и готов страдать за свои убеждения.
В C++ по сути, так и сделано, через указатели и таблицы виртуальных функций. В более продвинутых языках все подробности убраны под капот, дабы программисту было проще и выглядело красивее. Но вот почитал тут комменты - находятся люди, доказывающие, что можно сделать и БЕЗ наследования, хотя сами, по сути, просто реализовывают его вручную :)
Как на счет реализации полиморфизма через композицию + интерфейсы? Продолжая пример Сергея, ну ок, вынесли расчёт зарплаты в базовый класс "Сотрудник", а завтра выясняется что программисты (или некоторые из них) имеют ФОПы и выдача зарплаты у них принципиально другая. Что делать? Рисовать в базовом классе if, нарушая solid? Выносить эту логику в промежуточный класс? А что будет если будут еще такие отличия? Привет множественное наследование и mixin? ПыСы: ничего не имею против наследования, но по факту крайне мало бизнес-логики, которую можно было без опаски вынести в базовый класс. Базовый класс "Сотрудник" конечно же должен быть, на всякий случай
Пример с уборщицей Элементарно выносится подсчет зарплаты в SalaryCalculator В целом, наследование нужно, но лучше его избегать ввиду сильного повышения сложности кода с ним
@@olegkot3362 Даже в рамках одной и той же штатной единицы начисление ЗП может быть разным. И вычеты могут быть разными. Это я не говорю о премии и пр. Доп выплатах.
В этом плане очень и хороши интерфейсы с композицией. Создаем интерфейс SalaryCalculator. Делаем разные классы калькуляторов, а в классе сотрудника заводим атрибут SalaryCalculator и метод расчёта зарплаты через этот атрибут. И при повышении уборщицы до программиста просто меняем SalaryCalculator уборщицы на программиста.
Тоже, наверное, в каком то смысле про наследование... Что вы думаете над тенденциями, где считается не нужным использование классов вообще (типа классы в ооп - это ошибка (слова Дэвида Уэста)), а так же о книге Егора Бугаенко "Элегантные объекты"? Заранее благодарю за ответ)
Опять двадцать пять! И снова у нас базовый класс "Жывотное" с методами "есть", "спать", "ходить", "летать" и "гадить" от которого унаследованы все виды животных. Уже лет двадцать программистов тычут носом в этот пример и говорят: "Так делать не надо! Это худший пример ООП". За такие примеры уже пора пинать больно ногами в живот.
Абстрактный класс может содержать реализацию поведения. Интерфейсы - нет. По сути абстрактный класс - это обычный класс. но некоторые моменты своего поведения он оставляет полностью на откуп наследникам, никак в реализации не учавствуя, кроме указания что это надо реализовать. Интерфейс - это декларация поведения обьектов. т.е. описание публичных методов с которыми можно взаимодействовать, его не нужно путать с типом данных, которым является по сути класс.
Композиция решает проблему зарплаты Плюс что делать если программист не сотрудник фирмы, а он фрилансер...что в этом случае делать, как удалить всех наследников? Допустим такой случай. Есть продукт, как у него цену посчитать, сделать метод у базового класса? А что если несколько типов цен будет? Лучше вынести в АПИ класс для получения цены, не зависимо от класса продукта Полиморфизм достигается за счет имплементации интерфейсов...мы же не о C++ говорим, когда там интерфейсы это классы которые нужно наследовать. В Java интерфейсы реализуются, я не наследуются... Наследование хорошо ведет себя когда 100% код твой, а когда приложение многослойное, то наследование сильно связывает логику. Например модуль налогов хочет модифицировать цену, модуль специальной цены хочет вписаться, плюс модуль цен от другого производителя тоже хочет модифицировать... Получается на 1 класс 3 наследника...и кто кого будет наследовать? Представим описанного сотрудника...и вспомним I из SOLID как будет выглядет 10 мелких интерфейсов в этом случае, все на одном классе. В случае с active record все создают модели = таблице, в результате божественные модели получаются, добавить туда еще наследования и получается 5 слойная божественная модель Конечно же, если создавать модельки очень и очень мелкими, бить их на valueObject, то однозначно вариантов для наследования будет существенно больше...но опять же, через композицию классов можно добиться более гибкого и стабильного решения, хотя оно будет существенно сложнее Я говорю с точки зрения e-commerce enterprice, чтобы было понятно, тут специфика такая, что есть ограниченное количество агрегатов, условно до 10 на весь проект, и есть 500-900 модулей которые хотят на эти модели как-то накинуть свою логику... В системах с более тонкими моделями и меньшей модульностью уверн что наследование будет заходить как удобное и надежное решение...
Мне интересно надо ли создавать базовый класс сотрудника чтобы наследовать его на конкретных сотрудников, то что наследуюется иногда надо выносить в поля конечных классов как удобный объект который чтото умеет, возможно еще и делится по типам. Ради чего наследовать? чтобы максимум добавить поля данных применимые к любым типам сотрудников и все, если добавить функционал доступный всем типам сотрудников из базового класса то рано или поздно все поедет) через всякие геттеры сеттеры и динамиккасты) Все зависит от конкретных задач, иногда это удобно. При ООП код только разрастается, потому что парадигма стимулирует к этому, и в тоже время нужно работать объектами.
Автор часто апеллирует к тому, как плохо писали в 80-х без наследования, но сам застрял в 90-х. Тогда была повальная мода на вариант ООП с наследованием, и Джава дитя той эпохи (как и C++). Если всю жизнь пишешь на Джаве, в которой из инструментов только единичное наследование, то мозг атрофируется и даже не может предположить, что может быть что-то еще. Как говорится, если в руках молоток, то все кажется гвоздями. Например, полиморфизм можно делать без наследования с помощью 1) structural typing + embedding 2) pattern matching на algebraic data types 3) entity component system 4) multimethods 5) стратегии через интерфейсы 6) traits как в Rust. Конечно, в Джаве ничего этого нет, или требует много писанины. Вообще, при наследовании чаще чем всегда встречается архитектурная дичь вроде приведенного в пример Employee::calculateSalary(). Это не ответственность сотрудника определять какая у него зарплата. Этим занимается бухгалтерия, и тут нужен поиск стратегии на основе данных сотрудника. Т.к. обязанностей/бонусов у сотрудника может быть несколько и при расчете зарплаты нужно учитывать особенности бухгалтерии, которые являются ответственностью бухгалтера (отпускные, больничные, авансы, премии и т.д.). Это полная дичь совать такую логику в класс сотрудника. Автор скорей всего назовет иное "процедурным программированием" (а значит, плохо), но вариант использовать внешний сервис расчета более архитектурно верен и более масштабируем (как с точки зрения рефакторинга, если добавляются новые правила; так и архитектуры at scale, т.к. сервис расчета можно вынести в отдельный микросервис и масштабировать независимо, напр. добавить несколько воркеров-инстансов)
Не так плохо наследование, как злоупотребление им. Поэтому и говорят, что наследования лучше избегать, и говорят обычно про наследование классов с логикой и состоянием, а не про реализацию интерфейсов. При этом избегать не значит полностью отказаться, но подходить к использованию с максимальным пониманием, зачем в конкретной ситуации оно тебе нужно, и почему не подходят другие варианты, а лучший - именно этот. Потому что побочек всё-таки много при бездумном размножении цепочек наследования.
Само слово "наследование" неудачное для описания данного явления. Котэ наследуется от родителей и является таким же котэ, как предки. Если в природе было бы наследование как в программировании, то к котэ можно было прикрутить крылья или копыта, заменить метод мяуканье на лай (@Override). Американцы правильно называют extend, то есть класс не наследуется, а расширяет.
В этом разница между специалистами и не специалистами: Специалист применить в конкретном случае такой способ, который нужен именно здесь, без фанатизма. П.С.: Самый важный навык программиста это копипаст. Написал код, если его нужно переиспользовать - просто скопируй его и немного модифицируй)))
Ну так вообще непонятно, как общую функциональность выделять из классов без наследования (если не брать пример с godObject, про который Сергей в видео рассказал)
Главное не делать длинные цепочки наследования, иначе это превращается в проблему. А то мне тут один проект достался: задолбало искать у какого из родителей функция висит или константа, столько времени впустую уходило.
не понял насчет обсирания процедурного программирования из 80-х. Привет, Алан Кей релизнул первый ООП язык в 80-х в виде Smalltalk, если Java вышла в 90-х это не значит что это нечто новое, с точки изначальных замыслов ООП - Java это пародия на эту парадигму, посмешище на тот момент времени, жаль что с огромным маркетинговым бюджетом, интересно было бы посмотреть на smalltalk, который бы развивался
А куда же делась абстракция? Чистый ООП - это антипатерн. А именно - наследование. Можно вполне использовать композицию вместо наследования. Основная проблема наследования для прогера - это то, что надо знать все дерево. А это иногда очень трудоемко. А самое печальное - это то, что наследование очень плохо влияет на изменяемость кода. А хороший код должен быть хорошо изменяем.
Вот все говорят (и вы тоже их упомянули) про три принципа ООП. А откуда они взялись? Кто их сформулировал? Может быть, их не три, а два или четыре? Может быть, если не использовать наследование, то принципы ООП мы не нарушаем потому что принципы нам навязаны не идеологами ООП, а какими-то людьми, которые к ООП имеют весьма посредственное отношение? Да, википедия пишет, что их именно три и они именно такие, но пруфов на авторитетные источники по этому вопросу не видно. А то самое отношение "is a" вполне успешно реализуется имплементацией интерфейса. И раз уж на то пошло, то понятие "все сотрудники" включает в себя всех людей, которые как-то оказались в массиве "штат". И для того, чтобы стать сотрудником компании нужно имплементить интерфейс HomoSapiens. Ну и все остальные примеры - они тоже про интерфейсы. Жрёт, срёт, гадит по углам - это интерфейсы.
Возможно, это уже упоминали, но... Ведь в Go нет наследования, зато есть утиная типизация, позволяющая творить полиморфизм. На нём написаны очень сложные системы(минимум docker и kubernetes), и на нём создаётся всё больше программ вне enterprise сектора(я затрудняюсь определить понятие enterprise более конкретно, но думаю, Вы поняли меня). Почему он так успешен в этом направлении, обходясь без наследования?
Потому что есть его имитация в виде встраивания структур в структуры, в общем, си лайк ооп стайл. Неудобно до жути, но зато удобная асинхронщина, поэтому и сделали на нем подобные системы, плюс стильно, модно, молодежно, да еще и быстро.
Серёга, не передергивай. Контекст отказа от наследования другой: Мы не хотим работать с классами-матрицами, мы хотим работать с экземплярами, инстансами. Мы хотим строить объекты на лету. На фиг наследование, дайте удобный рефлекшн! Или прототип и lodash
нет. Реализация интерфейса не считается наследованием. Под наследованием подразумевается использование дочерним классом свойств и методов родительского класса, интерфейс же позволяет только декларировать что данный обьект обязан иметь реализованым такое то поведение
@@arthurfonzerelli6484 По сути да. Но с этим надо аккуратнее, иначе все может превратится в говнокод пострашнее чем "все наследуется от всего". В больших коммерческих проектах без наследования никак не обойтись, а интерфейсы больше используются для построения зависимостей. Если все строить чисто на интерфейсах в проекте, у которого исходники занимают несколько сотен мегабайт, то там можно сума сойти
Да. После лабораторок на Паскале и Си/Си++ и упором преподов на функциональщину, смотря на ООП в C# или той же Java задаешься вопросом, господи, это гениально. Интересно, на смену ООП придет ли когда-то другая парадигма? P.S. Сергей, может поразмышляете видосик на тему того, умрет ли когда-то парадигма ОПП?) Но, наверное это уже совсем другая история...
@@yurim7756 про ненужность программистов это такой же трёп дураков как и ненужность бухгалтеров. Бухгалтер будет, вопрос у кого, и чьи интересы он будет защищать. Если не хочешь втыкаться в кассовые разрывы то должен быть рядом бухгалтер. Если хочешь нормальную программу - её должен придумать , написать и отладить человек.
а не придется ли тогда создать библиотеку интерфейсов, чтобы интегрировать каждый нужный в нужный класс? чем это будет отличаться от обращения к процедуре из библиотеки для тех же ситуаций, когда мы интегрируем интерфейс в класс? удобство лишь в том, что не будем передавать объект класса? или как мы будем хранить библиотеку интерфейсов, если у них есть общие цели, раз мы не хотим выносить их в общий класс предок? не порождает ли это тех же проблем, только красивый вызов псевдонепроцедурный?
может запутанно сказал, но давайте предположим, что нам всё таки понадобилось вынести десяток важных общих функций в один класс предок. Да вся детвора будет обращаться к этим функциям. Утверждаем что это некрасиво и лучше создадим десяток интерфейсов и каждый из них интегрируем в каждый класс потомка. Да? Это и есть решение озвученной проблемы?
Видимо подразумевается, что в реальном мире условные автомобили А2 автоматически меняют колеса когда в чертеж базовой модели А1 внесли изменение. Или когда родители научились кататься на лыжах то все их текущие и будущие дети тоже умеют кататься на лыжах. Наследование по всюду, если присмотрется
В чём проблема использовать интерфейсы, а не классы? Тогда мы избавляемся от проблем наследования, и используем полиморфизм. Если я не прав, то поправьте
Интерфейсы декларируют ожидаемое поведения класса, ряд проблем наследования они не решают, например, интерфейс не хранит общий код (с этим в java можно поспорить, потому что есть default методы, но и с ними не всё гладко), интерфейс не позволяет объявить не публичные методы (например, шаблон "песочница" построен как раз на protected методах), интерфейс не позволяет объявить состояние объекта (если мы хотим расширить поведение и состояние какого-то объекта, не изменяя при этом класс объекта, то интерфейс нам не поможет). Есть мнение, что наследование лучше всего использовать не в глубину, а в ширину
@@denys.martyniuk абстрактный класс, это абстрактный класс, а интерфейс это что-то на подобии класса, сущность в которую можно написать только методы и то без их реализации. В PHP еще есть трейты, вот ими можно расширить класс и без наследования просто подключив трейт там где надо
Наследование - инструмент, который вполне можно использовать иногда. Хейтить его не надо, его надо просто уметь готовить и не пихать везде, где нужно и не нужно. Но я не согласен, что его надо возводить в какой-то абсолют и с ироничной улыбкой в снисходительном тоне говорить о тех, кому не нравится наследование, и кто использует его крайне редко и только там, где оно действительно эффективней всех других методов.
Зашел чтобы убедится, что вы все еще Сергей Немчинский
Я только для этого захожу на его видосы. До самого конца смотрю чтоб точно убедиться что это он
Напишите в личку когда Сергей Немчинский уже не будет Сергеем Немчинским
@Дмитрий Давыдовпомню была 10 нет назад.
Всю жизнь использую наследование. И отец мой использовал, и дед, и прадед.
...и Object
Сына выгнали из семьи за функциональщину.
ох уже эти староверы.
У вас это наследственное значит
Бог наследовал и нам велел
Кто-нибудь в курсе как ведущего зовут?
Оскар Сукинцев
Немчей Сергинский
Его не зовут, он сам приходит
Вроде Игорь, но могу ошибаться
А, это тот чел из Extreme Code
Сергей, это крутой видос! Скажу по себе, иногда бывает застрянешь на каких-нибудь простых темах, и вот именно такие подробные объяснения расставляют все по полочкам в наших головешках)
Подытожу кратко, что сказал Сергей в видео: - "Применяйте наследование там где оно действительно нужно, если в нем нет смысла используйте делегирование. Больше думайте и применяйте все по назначению, а не потому, что так модно"
Спасибо
На моей практике , можно писать исключительно используя композицию . Как говорил Блох: наследование - сильная связь. А она попросту в большинстве случаев не уместна .Но это не значит , что наследование - зло. Все зависит от конкретной задачи, для этого и существует декомпозиция предметной области.
@@kitten-free А где сразу хочется композицию - агрегацию
Почему не упоминают , что есть наследование только интерфейса, а есть наследование реализации. Первую никто вменяемый вроде особо и не критикует. А вторую советуют не применять.
Потому что это не наследование а имплементация
@@МаксимВеснин-ш3р Есть имплементация - это когда Объект реализует какой то функционал, а есть наследование интерфейсов - это когда интерфейс наследует поведение другого интерфейса.
Спасибо Сергей, с вами всё становится проще.
"полиморфизм тоже невозможен без наследования" расскажите кто-нибудь автору про шаблоны в c++, про трейты в rust... Дядька крепко застрял в модных концепциях ООП из начала 90-х.
И встраивание в Go.
Это называется java головного мозга. Все, что не джава - все плохо
Если слушать внимателольно, то можно понять, что автор про template в с++ знает и упоминает... hate головного мозга)))
шаблоны c++ это же вроде только статический полиморфизм?
Полезный ролик. Про нормализацию баз данных тоже нубам расскажите, чтоб не портились протоколы измерений при удалении типа двигателя в справочнике двигателей.
Второй лайк за упоминание Foxpro - отличная система была! Пришлось в 90-е с ней немало поработать. Даже низкоуровневые операции были доступны.
Вот это настоящие эмоции! Круто!
я не программист (для себя пока теорию изучаю). Пересматривал несколько раз и почему-то именно с вашего видео начал понимать.
5:32 да именно так, в функциональной парадигме всё иммутабельно!
Используем только структуры, никаких объектов. Если конечно не пишем на Scala, но и там стараемся от иммутабельности избавится
Пример с сотрудниками неудачен. Сотрудник - это сотрудник, а программист - это роль сотрудника. Таких ролей может быть много. Сотрудник может быть и программистом, и аналитиком одновременно. Т.е. программист - это не наследник сотрудника.
А программист не сотрудник? Или аналитик не сотрудник?
@@sardaucar это еще и человек, наследующийся от млекопитающего - ты уверен что от этого объекта на роли программиста требуются все методы, включая жрать(), срать(), спать()?
от него требуются методы которые ему характерны и да, он может имплементировать множество интерфейсов, один из которых скажем сотрудник, с методами ходить_на_работу(), получать_зарплату(), участвовать_в_корпоративах(), а другой интерфейс - программист, с методами писать_код() дебажить(), задавать_глупые_вопросы_в_интернетиках().
то же самое и аналитик.
но вот допустим нюанс - а если аналитик удаленный? тогда чо? будем ему перепиливать метод ходить_на_работу()? а если у нас 100500 удаленных ролей - всем им перепиливать? упс, да? :))
поэтому наследование - ну такое.
тут просто Сергей застрял немного во времени и сейчас в современных языках уже появились возможности безболезненно пилить реализации для интерфейсов.
Ассоциация, агрегация и композиция ни чем не хуже, наследования, они просто для разного. Но почему-то наследование вынесено в основной принцип, а они - нет. Как только новичок выучивает наследование, то у него сразу замдиректора наследует от директора, потому, что он его старший сын, а метод начисления зарплаты находится у уборщицы, а не у бухгалтерии.
да, так было, но давно. Сейчас наоборот - новички не используют наследование ВООБЩЕ.
Это вопрос уже "Насколько развито абстрактное мышление". Многие люди не умеют выделять из объектов общие свойства и создавать родителя с этими свойствами. Есть директор, есть зам. больше ничего не надо, директор главнее, значит он родитель.
@@SergeyNemchinskiy Маразм проходит циклическое развитие, то мода на одно, то на другое. Это редукция сложного к одному, она конечно нужна, но первые 10 лет выходит что выходит))) ну и это смотря в каком языке еще, разные языки на разной стадии моды в данный момент
@@shitposting_box начинать нужно с Платона и Аристотеля
@@TimurShemsedinov согласен :)
В целом согласен с вами. Хочу ли добавить пару моментов.
Возможно стоит разделять наследование на наследование и реализацию. Добрую часть паттернов можно организовать используя реализацию (от интерфейсов или абстрактных классов).
Стоит также держать в голове мысль, что наследованием нужно пользовать с осторожностью, так как это самый жесткий вид взаимосвязи между классами.
Не подумайте, это не хейт наследования, это попытка сказать, что нужно учиться проектировать программы в ООП стиле)
Сергей! От души рад, что позволили подписаться на Вас, влекущее автоматическое уведомление на смартфон и позволяющее на ровном месте ЛикБез-сть! и по-брацки.
О, по ООП вовремя видосики пошли.
Наверно это самая важная часть в книге банды четырех: специфицирование интерфейсов объектов и далее (стр 27, docs.google.com/file/d/0B6GuCegBf3X3Tm1rZl9BUTduQm8/edit). Они четко разделяют понятия интерфейс/тип (причем это не только то, что находится после слова interface) и реализацию/класс. "... интерфейсы ... фундаментальны". Только после разделения этих понятий в голове становятся возможны: декораторы, адаптеры, композиции и др. Если же рассматривать их в одной куче, то получаются что без наследования (в целом) невозможно строить полиморфизм (большинство паттернов его частный случай). На самом деле его невозможно строить без разделения между несколькими классами интерфейсов или попросту наследования классами интерфейсов.Пример в видео с зарплатой приведен не очень корректно. Обычно этот расчет занимает сотни и тысячи строк кода и обычно его хочется реализовывать где-то еще, а не в сотруднике. Наследование интерфейсов позволит реализовать его во вне понятия сотрудник, а наследование реализаций заставит вас сделать божественный базовый класс. Отсюда, на мой взгляг, и проблема наследования реализаций, но к наследованию интерфейсов это не имеет ни какого отношения.
Огонь, жду ещё такую тематику
Я от перешел с ангуляра на "современный" реакт, и полностью понимаю вашу боль, Сергей)
Без наследования в принципе нельзя писать приложения под тот же Android, например, где все элементы UI наследуются от View. Хочешь запилить кастомный контрол - наследуешься от View (или от любых других его потомков, не помеченных как final). Забавно, что в Android кнопки (Button) наследуются от TextView, и, если глянуть в исходники TextView и Button, можно увидеть, что в TextView около 13000 строк Java-кода, в то время как в Button не набирается и 200 (бóльшая часть из которых - javadoc).
Большое спасибо за такой полезный контент!
Ок, я всё понял. Если нужно будет делать приложение с работниками и сотрудниками - использовать наследование.
На самом деле хрень редкая.
На предприятиях где этот расчет зарплаты реально применяется запросто бывают ситуации когда сотрудник 10 дней работает токарем, 5 дней грузчиком на погрузке вагонов, 2 дня сварщиком и 3 дня белит потолки.
Это обычная, рядовая ситуация. Успехов вам с вашим ООП.
Или с капибараами
в большинстве случаев будет достаточно одного класса сотрудников, а должность - значение его поля
Если нужно будет делать приложение с работниками и сотрудниками - использовать 1С ;-)
@@TwilightSun32 для рынка постсовка в основном так и делают, но Немчинский занимается подготовкой спецов в первую очередь на проекты для западных заказчиков
Чётко и понятно.
Ах да. Сергей. Киньте людям ролик про интерфейсы и про их полезность. А потом и про дженерики в пром коде перетрите немного. Вас просто тут расцелуют.
Поддерживаю.
Спасибо большое за разъяснение. 👍
Я всегда говорю, что надо зреть в корень и понимать, как что работает и почему. Нам ещё в институте приводили в пример такую историю.
Женщина нарезала бекон маленькими полосками перед тем как обжарить его на сковородке. Её спросили зачем она это делает? Почему просто не жарить его так как он порезан в упаковке? Она сказала, что так делала её мама. А мама сказала, что так делала её мама. Когда у бабушки, спросили тоже самое, она сказала, что в её время сковородки были очень маленькие и длинные полоски просто не помещались.
Это конечно всего лишь история, но суть передаёт. Если вы не понимаете зачем вы что-то делаете, возможно вы делаете это в пустую. Так как возможно причина этого уже не существует.
Вы сейчас такую тему затронули, которая касается всей нашей жизни)) Всякие суеверия, вера в сверхъестественное и тому подобное так же наследуются))
@@0imax это да, традиции в особенности.
сколько раз в ролике использовалось слово "композиция" ? так не честно же
из субтитров:
"наслед" 37 раз
"делегирова" 2 раза
"компо" 0 раз
Расскажите, Сергей, пожалуйста, и о других принципах ООП!
Ну вот с повышением уборщицы до программиста, если это разные классы, даже с общим предком, всё равно надо один объект убить, другой создать. Правда, ко мне, как к программисту PHP, это всё не относится :))) У меня объекты так долго не живут :)))
Когда Сергей сказал, что использование полиморфизма без использования наследования невозможно, где-то в тёмном уголке тихонечко заплакали интерфейсы... от смеха...
Если учитывать, что интерфейсы выросли из полностью абстрактных классов для решения проблемы множественного наследования, то это тоже в каком-то смысле наследование, пусть и названное в джаве как implements.
@@0imax А почему они "выросли"? Потому что у наследования есть проблемы. При этом отказываться от наследования полностью тоже считаю идиотизмом. Оно нужно там, где оно нужно. Главное - не возводить его на пьедестал.
Почему-то программисты, когда приводят пример другого сотрудника, чаще всего упоминают уборщицу. Видимо это тот сотрудник, который чаще всего отвлекает их от работы ... или прокрастинации.
просто условное разделение по типу "программист" - "не программист". Ну а про начальство просто молчат чтобы не сбиться с темы на обсуждение личности.
Просто программисты, почему-то, привыкли и продолжают считать себя обслуживающим персоналом, наравне с уборщицей, типа бизнес приносит деньги, клерки и менеджеры главное звено... давно это уже не так. Снимайте тишорты, надевайте версаче. Дальше действовать будем мы (с) В. Цой
Поставлю себе на рабочий ярлык на это видео. Чтобы в любой непонятной ситуации по принципу Наследования, оно было где надо.
Пример с сотрудниками не корректен. По сотрудникам как раз стоит юзать композицию "сотрудник" + вложенный объект "функциональная спецификация" . И не забывать в "сотруднике" про атрибутивный состав (положение в штатке, расположение рабочего места и т.д.)
Если же все отдельные классы сотрудников наследуются от базового, то изменение штатки - убийство существующего объекта и создание нового.
Опять же, начальник отдела программистов может быть иле не быть программистом, но менеджером он быть обязан.
Например, у нас базовый класс сервиса, а остальные реализации мы передаем в качестве агрегированных объектов. И вот у нас без наследование точно так же всё работает)) Только удобнее менять) И можно проверять реализации и тестировать их не зависимо от базового класса. А базовый класс не зависимо от реализации так называемых наследников.
Теперь сделайте видео про Полиморфизм.
th-cam.com/video/Ay_GwOQWPs8/w-d-xo.html
И про полиморфные вирусы!
Создатель ООП Алан Кэй об ООП:
"ООП для меня это сообщения, локальное удержание и защита, скрытие состояния и позднее связывание всего. Это можно сделать в Smalltalk и в LISP."
Всё.... Остальное наворотили авторитетные люди. Поэтому, если кто-то говорит о проблемах наследования, то не стоит ссылаться на догмы о трёх принципах...
позднее связывание через наследование реализуется же
Только вот какая разница что там думал чел, язык которого не взлетел от слова совсем, а то ооп, которое в итоге позволило создать колоссальные приложения, не похоже ни на йоту на ооп его "создателя".
Есть программа, которая написана на фортране, моделирующая перенос фундаментальных частиц. Софт коммерческий, и широко используется для расчётов. Так вот там тысяч 50 строк кода, не меньше. Она в принципе поставляется с исходниками для возможной ее модификации. Чтобы ее модифицировать, надо перелопатить охрененную тучу кода!!! Зато фортран. С кусками Си. Возможность подключать определенные библиотеки физических величин.
наследование использую только в написании каких-то системных вещей или системных (не связанных с конкретной бизнес логикой) либ для приложения, где логика понятна и вряд ли поменяется. практика показала, что наследовать объекты реального мира, то есть брать данные из ТЗ и делать из этого какую-то иерархическую структуру как правило приводит в перспективе к говнокоду и усложнению поддержки, из-за того что бизнес меняется очень быстро. Сегодня программист - это сотрудник, завтра он уже "удаленный сотрудник", а еще вот есть "удаленный на полставки". В итоге сегодня мы под ТЗ делаем красивую стройную модель, а завтра малейшее изменение приводит к тому, что нам надо рефачить все дерево и все зависимые от либы проекты. Какой выход? под конкретные проекты по-разному придумываем, но часто стараемся делать так, чтобы переиспользование кода было минимальным как раз. Если у меня есть похожая логика, я ее не выношу в отдельный модуль или библиотеку, а прямо копипащу с небольшими изменениями. В итоге новый человек на проекте или я через год, когда нужно будет логику поменять, увидит, что определенный метод используется всего в 1 или 2 местах в коде и описывает небольшой кусочек (SRP привет), чем код, который растянут на 10 наследников и еще переопределен в куче мест. Не утверждаю, что наследование плохо, просто на практике оказалось выгоднее стараться обходиться без него, ну или скорее не совать его везде.
П.С. у S0ERа есть хорошее видео на тему копипаста кода. согласен с ним, что копипаст, это не всегда зло. Если логика просто похожая, но логически другая - здесь копипаст это не дублирование кода, так как логически эти методы про разное и похожий код не является дублированием.
Скорее всего это означает что вместо введения свойств сотрудника, которые описывают способ работы и способ оплаты добавили классы наследники. Не нужно наследовать на каждый чих.
@@yuriy.kostenko ну об этом я и говорю, мы не описываем реальный мир наследованием.
что касается такого подхода как введение свойств вместо наследования. он тоже достаточно дискуссионный. допустим у меня метод, который работает только с удаленными сотрудниками. Если у меня не будет соотв. класса под это, мне нужно в рантайме проверять на галочку "а передали ли мне удаленного сотрудника или же потребитель моего кода мне подсунул ненужные данные". при введении соотв. класса мы это проверяем на этапе компиляции - потребитель просто не засунет в наш метод ничего.
@@Dimonina В чем проблема вынести подобные алгоритмы работы в отдельные классы-стратрегии? Как раз-таки наследование и нужно для того, чтобы решить описанную тобой проблему. Выносишь поведения "удаленный сотрудник" и "сотрудник на полставки" в отдельные классы-стратегии и внедряешь зависимость в свой класс. Если в будущем надо будет добавить еще какой-то тип сотрудника, ты просто расширяешь свой код через наследование, а не изменяешь уже разработанный и протестированный, и не пишешь огромную пелену кастов и условных операторов для определения типа сотрудника. Если возникла необходимость комбинировать, например "удаленный сотрудник на полставки", то оборачиваешь это все в декоратор. Я не вижу тут никакой проблемы наследования, а только типичную ситуацию, в котором оно полезно
@@МаксимМалышев-м6ы по тексту сложновато понять конкретную реализацию, я бы в код глянул. концепт понятен, но вот реализация может быть разной. если будет время и желание, посмотрел бы пример. возможно говорим об одном и том же, просто с разных углов
@@Dimonina по моему мнению как раз именно наследованием мы описавыем реальный мир. И свойства обьектов в реальном мире очень даже проверяются. Типичный пример касса в магазине. Касса работает с людьми, а не с покупателями (потому что еще не факт что человек станет покупателем, вдруг его свойство "количество денег" не достаточно для того что он приволок на кассу) и проверяет много свойств, в том числе If(покупает алкоголь && свойство Возраст > 18) OK else() "а ну брысь!"
Как-то вот так. )
Сергей, здравствуйте. Спасибо за видео. Одна просьба. Не могли бы вы выводить на экран название английских терминов, которые используете. Не все удается разобрать
Судя по моему опыту, могу сказать скорее вопрос о необходимости использовании ООП в определенных задачах. При драйверном уровне приложений ООП может быть через-чур избыточен, а как следствие большая часть парадигм просто не будет использоваться. С другой стороны есть множество задач где ООП является оптимальным вариантом использования. А еще появляется такая интересная особенность что раз язык позволяет использовать парадигму, то ее порой стремятся использовать просто потому что такой инструментарий есть. Выводы каждый прочитавший думаю сделает сам. Зачем наследование Именно в ооп - ответ дан автором видео.
Я вот рассматриваю Java, как будущее хобби и ещё не написал ни одной серьёзной программы в жизни. Но с каждым подобным роликом т.н. "порог вхождения" для меня становится существенно ниже. Спасибо.
Есть такой признак неиспользования полиморфизма и наследования: это наличие больших, разветвленных, иногда многовложенных switch/case или if/then/else .
А для приведения этого безобразия в человеческий вид есть несколько паттернов для разных ситуаций)
Полезно, но почему не прозвучвла "КОМПОЗИЦИЯ' в контексте альтернативы наследования?
прозвучало. Делегирование
Касательно заключительной части ролика, про то, что наследование следует использовать в тех местах, где оно имеется в реальном мире.
Читал на хабре статью и соответственно слышал мнение, что само по себе мышление программиста реальными сущностями - бич.
И нужно наоборот стараться мыслить структурами данных и абстрактными объектами, которыми в первую очередь удобно мыслить и действовать в данном случае.
Интересно мнение.
Ого, я стал настолько старым, что начал узнавать что-то новое. Например, в этом ролике услышал, что появилась мода считать, что наследование не нужно )
А патерны, в теории, можно и без наследования делать. Полиморфизм можно сделать указателями на функции (делегатами). Ах, да, я ж сишарп программист. Но на джаве там тоже как-то это можно сделать, подобие. Наверное. Если в классе без наследования хранить поле - указатель на функцию, и его подменять, для разных объектов получая разное поведение, то получится и полиморфизм без наследования, и остальное можно постаравшись, поизвращавщись, получить. Не знаю зачем. Может кто педантично ненавидит наследование и готов страдать за свои убеждения.
В C++ по сути, так и сделано, через указатели и таблицы виртуальных функций. В более продвинутых языках все подробности убраны под капот, дабы программисту было проще и выглядело красивее. Но вот почитал тут комменты - находятся люди, доказывающие, что можно сделать и БЕЗ наследования, хотя сами, по сути, просто реализовывают его вручную :)
Как на счет реализации полиморфизма через композицию + интерфейсы? Продолжая пример Сергея, ну ок, вынесли расчёт зарплаты в базовый класс "Сотрудник", а завтра выясняется что программисты (или некоторые из них) имеют ФОПы и выдача зарплаты у них принципиально другая. Что делать? Рисовать в базовом классе if, нарушая solid? Выносить эту логику в промежуточный класс? А что будет если будут еще такие отличия? Привет множественное наследование и mixin?
ПыСы: ничего не имею против наследования, но по факту крайне мало бизнес-логики, которую можно было без опаски вынести в базовый класс. Базовый класс "Сотрудник" конечно же должен быть, на всякий случай
Пример с уборщицей
Элементарно выносится подсчет зарплаты в SalaryCalculator
В целом, наследование нужно, но лучше его избегать ввиду сильного повышения сложности кода с ним
Ага с кучей условий в зависимости от того какая должность, какой договор и пр. 😂
Элементаррно все методы выносятся в общий класс... Черт, я ровно об этом и рассказал в видео.
@@SergeyNemchinskiy это не общий класс, а класс, который отвечает за логику начисления зарплат. Он может быть применен и для других классов...
@@olegkot3362 Даже в рамках одной и той же штатной единицы начисление ЗП может быть разным. И вычеты могут быть разными.
Это я не говорю о премии и пр. Доп выплатах.
В этом плане очень и хороши интерфейсы с композицией. Создаем интерфейс SalaryCalculator. Делаем разные классы калькуляторов, а в классе сотрудника заводим атрибут SalaryCalculator и метод расчёта зарплаты через этот атрибут. И при повышении уборщицы до программиста просто меняем SalaryCalculator уборщицы на программиста.
Реальный мир удобнее описывать через теорию множеств, чем через наследование
То есть как группы?
@@yuriy333, множества объектов, реализующих методы. Группы - это другая штука.
Сергей (надеюсь, что все ещё Немчинский) нужно видео про все 3 элемента ООП)
так он, кажется, про все рассказывал
Тоже, наверное, в каком то смысле про наследование...
Что вы думаете над тенденциями, где считается не нужным использование классов вообще (типа классы в ооп - это ошибка (слова Дэвида Уэста)), а так же о книге Егора Бугаенко "Элегантные объекты"?
Заранее благодарю за ответ)
Сергей, круто. Вроде, ооооп понял.
Расскажите об ESP32.
да как же так - полиморфизм без наследования не возможен? возможен! может, конечно, в java по другому, но вроде мы обсуждаем саму концепцию ООП)
видео о наследовании extends видео от Сергея Немчинского))
Опять двадцать пять! И снова у нас базовый класс "Жывотное" с методами "есть", "спать", "ходить", "летать" и "гадить" от которого унаследованы все виды животных. Уже лет двадцать программистов тычут носом в этот пример и говорят: "Так делать не надо! Это худший пример ООП". За такие примеры уже пора пинать больно ногами в живот.
Критикуешь? Предлагай!
Свой идеальный пример в студию! А мы уж тут оторвемся. Мы ж любители искать соринки в чужих глазах.
то-то лет двадцать не код, а сплошные фекальные сталактиты везде
Ого. Я понимаю о чем речь 😄
я тоже понял но не думаю что всё 🤧
Здравствуйте, а нет ли у вас видео по интерфейсам, особенно зачем они нужны и какая разница между ними и абстрактными классами, спасибо
Абстрактный класс может содержать реализацию поведения. Интерфейсы - нет. По сути абстрактный класс - это обычный класс. но некоторые моменты своего поведения он оставляет полностью на откуп наследникам, никак в реализации не учавствуя, кроме указания что это надо реализовать. Интерфейс - это декларация поведения обьектов. т.е. описание публичных методов с которыми можно взаимодействовать, его не нужно путать с типом данных, которым является по сути класс.
@@deniskoroliov1039 спасибо большое
Что за ноутбук у Сергея? Подскажите, пожалуйста.
Про ООП где-то был классный пример с котятами. Кажется, на хабре.
В языках где объекты мутабельны наследование не нужно. Да и в старых языках в большинстве случаев наследование можно заменить на делегирование.
Разве для работников не лучше прописать интерфейс?
Композиция решает проблему зарплаты
Плюс что делать если программист не сотрудник фирмы, а он фрилансер...что в этом случае делать, как удалить всех наследников?
Допустим такой случай. Есть продукт, как у него цену посчитать, сделать метод у базового класса?
А что если несколько типов цен будет?
Лучше вынести в АПИ класс для получения цены, не зависимо от класса продукта
Полиморфизм достигается за счет имплементации интерфейсов...мы же не о C++ говорим, когда там интерфейсы это классы которые нужно наследовать. В Java интерфейсы реализуются, я не наследуются...
Наследование хорошо ведет себя когда 100% код твой, а когда приложение многослойное, то наследование сильно связывает логику.
Например модуль налогов хочет модифицировать цену, модуль специальной цены хочет вписаться, плюс модуль цен от другого производителя тоже хочет модифицировать...
Получается на 1 класс 3 наследника...и кто кого будет наследовать?
Представим описанного сотрудника...и вспомним I из SOLID
как будет выглядет 10 мелких интерфейсов в этом случае, все на одном классе.
В случае с active record все создают модели = таблице, в результате божественные модели получаются, добавить туда еще наследования и получается 5 слойная божественная модель
Конечно же, если создавать модельки очень и очень мелкими, бить их на valueObject, то однозначно вариантов для наследования будет существенно больше...но опять же, через композицию классов можно добиться более гибкого и стабильного решения, хотя оно будет существенно сложнее
Я говорю с точки зрения e-commerce enterprice, чтобы было понятно, тут специфика такая, что есть ограниченное количество агрегатов, условно до 10 на весь проект, и есть 500-900 модулей которые хотят на эти модели как-то накинуть свою логику...
В системах с более тонкими моделями и меньшей модульностью уверн что наследование будет заходить как удобное и надежное решение...
Мне интересно надо ли создавать базовый класс сотрудника чтобы наследовать его на конкретных сотрудников, то что наследуюется иногда надо выносить в поля конечных классов как удобный объект который чтото умеет, возможно еще и делится по типам. Ради чего наследовать? чтобы максимум добавить поля данных применимые к любым типам сотрудников и все, если добавить функционал доступный всем типам сотрудников из базового класса то рано или поздно все поедет) через всякие геттеры сеттеры и динамиккасты) Все зависит от конкретных задач, иногда это удобно. При ООП код только разрастается, потому что парадигма стимулирует к этому, и в тоже время нужно работать объектами.
можно же сделать интерфейс расчета зарплаты и конкретные калькуляторы в качестве зависимости передавать?
Про голубя помню картинку 🤣👍
Автор часто апеллирует к тому, как плохо писали в 80-х без наследования, но сам застрял в 90-х. Тогда была повальная мода на вариант ООП с наследованием, и Джава дитя той эпохи (как и C++). Если всю жизнь пишешь на Джаве, в которой из инструментов только единичное наследование, то мозг атрофируется и даже не может предположить, что может быть что-то еще. Как говорится, если в руках молоток, то все кажется гвоздями. Например, полиморфизм можно делать без наследования с помощью 1) structural typing + embedding 2) pattern matching на algebraic data types 3) entity component system 4) multimethods 5) стратегии через интерфейсы 6) traits как в Rust. Конечно, в Джаве ничего этого нет, или требует много писанины. Вообще, при наследовании чаще чем всегда встречается архитектурная дичь вроде приведенного в пример Employee::calculateSalary(). Это не ответственность сотрудника определять какая у него зарплата. Этим занимается бухгалтерия, и тут нужен поиск стратегии на основе данных сотрудника. Т.к. обязанностей/бонусов у сотрудника может быть несколько и при расчете зарплаты нужно учитывать особенности бухгалтерии, которые являются ответственностью бухгалтера (отпускные, больничные, авансы, премии и т.д.). Это полная дичь совать такую логику в класс сотрудника. Автор скорей всего назовет иное "процедурным программированием" (а значит, плохо), но вариант использовать внешний сервис расчета более архитектурно верен и более масштабируем (как с точки зрения рефакторинга, если добавляются новые правила; так и архитектуры at scale, т.к. сервис расчета можно вынести в отдельный микросервис и масштабировать независимо, напр. добавить несколько воркеров-инстансов)
И потом фиксить баги годами в миллиарде файлов и репозиториев, вместо фикса одного единственного класса
Не так плохо наследование, как злоупотребление им. Поэтому и говорят, что наследования лучше избегать, и говорят обычно про наследование классов с логикой и состоянием, а не про реализацию интерфейсов. При этом избегать не значит полностью отказаться, но подходить к использованию с максимальным пониманием, зачем в конкретной ситуации оно тебе нужно, и почему не подходят другие варианты, а лучший - именно этот. Потому что побочек всё-таки много при бездумном размножении цепочек наследования.
Само слово "наследование" неудачное для описания данного явления. Котэ наследуется от родителей и является таким же котэ, как предки. Если в природе было бы наследование как в программировании, то к котэ можно было прикрутить крылья или копыта, заменить метод мяуканье на лай (@Override).
Американцы правильно называют extend, то есть класс не наследуется, а расширяет.
Уборщицу повысили до программиста - как входят в IT js'ники.
🤣👍
Подтвердите в письменном виде, что вы всё ещё Сергей Немчинский, а то остались сомнения
В этом разница между специалистами и не специалистами: Специалист применить в конкретном случае такой способ, который нужен именно здесь, без фанатизма.
П.С.: Самый важный навык программиста это копипаст. Написал код, если его нужно переиспользовать - просто скопируй его и немного модифицируй)))
Ну так вообще непонятно, как общую функциональность выделять из классов без наследования (если не брать пример с godObject, про который Сергей в видео рассказал)
вот это действительно годно 👍
Главное не делать длинные цепочки наследования, иначе это превращается в проблему. А то мне тут один проект достался: задолбало искать у какого из родителей функция висит или константа, столько времени впустую уходило.
Отсутствие наследования не означает копи-пастинга.
не понял насчет обсирания процедурного программирования из 80-х. Привет, Алан Кей релизнул первый ООП язык в 80-х в виде Smalltalk, если Java вышла в 90-х это не значит что это нечто новое, с точки изначальных замыслов ООП - Java это пародия на эту парадигму, посмешище на тот момент времени, жаль что с огромным маркетинговым бюджетом, интересно было бы посмотреть на smalltalk, который бы развивался
Только вот эта пародия позволила создать миллионы превосходных больших приложений, а смолток так и остался никому ненужным высером. Пара-пара-пам.
Котлин: интерфейсы со свойствами и дефолтными методами.
Немчинский: Наследуемся!
Сейчас в c# эту хрень запихали
@@artursveshnikov7668 наследование тоже когда-то было хренью
Звучит как будто в котлин изобрели абстрактные классы
@@СергейПресняков-о4р да, звучит именно так, для людей которые не понимают чем класс отличается от интерфейса
Блин, насколько точно!
Мы все любим говнокодить!
Как насчет того чтобы раскрыть такое понятие как плохой программист?
привет всем !!
А куда же делась абстракция?
Чистый ООП - это антипатерн. А именно - наследование. Можно вполне использовать композицию вместо наследования.
Основная проблема наследования для прогера - это то, что надо знать все дерево. А это иногда очень трудоемко. А самое печальное - это то, что наследование очень плохо влияет на изменяемость кода.
А хороший код должен быть хорошо изменяем.
А как же абстракция, посылка сообщений и повторное использование?
Забавно. Я раз 50 услышал: "Пишите на Java/C#", при том что он ни разу ни упомянул о них ни слова ...
Вот все говорят (и вы тоже их упомянули) про три принципа ООП. А откуда они взялись? Кто их сформулировал? Может быть, их не три, а два или четыре? Может быть, если не использовать наследование, то принципы ООП мы не нарушаем потому что принципы нам навязаны не идеологами ООП, а какими-то людьми, которые к ООП имеют весьма посредственное отношение? Да, википедия пишет, что их именно три и они именно такие, но пруфов на авторитетные источники по этому вопросу не видно.
А то самое отношение "is a" вполне успешно реализуется имплементацией интерфейса. И раз уж на то пошло, то понятие "все сотрудники" включает в себя всех людей, которые как-то оказались в массиве "штат". И для того, чтобы стать сотрудником компании нужно имплементить интерфейс HomoSapiens. Ну и все остальные примеры - они тоже про интерфейсы. Жрёт, срёт, гадит по углам - это интерфейсы.
Возможно, это уже упоминали, но...
Ведь в Go нет наследования, зато есть утиная типизация, позволяющая творить полиморфизм.
На нём написаны очень сложные системы(минимум docker и kubernetes), и на нём создаётся всё больше программ вне enterprise сектора(я затрудняюсь определить понятие enterprise более конкретно, но думаю, Вы поняли меня). Почему он так успешен в этом направлении, обходясь без наследования?
Потому что есть его имитация в виде встраивания структур в структуры, в общем, си лайк ооп стайл. Неудобно до жути, но зато удобная асинхронщина, поэтому и сделали на нем подобные системы, плюс стильно, модно, молодежно, да еще и быстро.
А разве полиморфизм это не обеспечение единого интерфейса типам?
Серёга, не передергивай.
Контекст отказа от наследования другой: Мы не хотим работать с классами-матрицами, мы хотим работать с экземплярами, инстансами. Мы хотим строить объекты на лету. На фиг наследование, дайте удобный рефлекшн! Или прототип и lodash
Полиморфизм возможен через реализацию интерфейсов. Реализацию интерфейсов можно считать наследованием?
нет. Реализация интерфейса не считается наследованием. Под наследованием подразумевается использование дочерним классом свойств и методов родительского класса, интерфейс же позволяет только декларировать что данный обьект обязан иметь реализованым такое то поведение
@@deniskoroliov1039 значит тезис "без наследования невозможен полифорфизм" неверен, потому что полиморфизм возможен через реализацию интерфейса
@@arthurfonzerelli6484 По сути да. Но с этим надо аккуратнее, иначе все может превратится в говнокод пострашнее чем "все наследуется от всего". В больших коммерческих проектах без наследования никак не обойтись, а интерфейсы больше используются для построения зависимостей. Если все строить чисто на интерфейсах в проекте, у которого исходники занимают несколько сотен мегабайт, то там можно сума сойти
@@arthurfonzerelli6484 Опять же. Наследование убирает проблемуц дублирования кода, интерфейсы ее создают
Да. После лабораторок на Паскале и Си/Си++ и упором преподов на функциональщину, смотря на ООП в C# или той же Java задаешься вопросом, господи, это гениально.
Интересно, на смену ООП придет ли когда-то другая парадигма?
P.S. Сергей, может поразмышляете видосик на тему того, умрет ли когда-то парадигма ОПП?)
Но, наверное это уже совсем другая история...
будет всë, ибо ложка к супу, лопата к огороду, а экскаватор строителям. Тру.
Конечно, придет. Я думаю придет. Эта парадигма придет и уволит всех программистов, потому что они уже будут не нужны. Надеюсь, что нескоро.
@@yurim7756 про ненужность программистов это такой же трёп дураков как и ненужность бухгалтеров. Бухгалтер будет, вопрос у кого, и чьи интересы он будет защищать. Если не хочешь втыкаться в кассовые разрывы то должен быть рядом бухгалтер. Если хочешь нормальную программу - её должен придумать , написать и отладить человек.
@@dmitriyvaulin Так я про сильный ИИ. Когда человек будет тем самым дураком )
@@yurim7756 Сильный ИИ можно создать хоть сейчас. Главное дать ему мотивацию и сенсоры. Если что могу поучаствовать в разработке.
Наследование в Java очень удобная вещь.
Повышаем уборщицу до программиста... Наследование... Вуаля! Все уборщицы стали программистами!
а не придется ли тогда создать библиотеку интерфейсов, чтобы интегрировать каждый нужный в нужный класс? чем это будет отличаться от обращения к процедуре из библиотеки для тех же ситуаций, когда мы интегрируем интерфейс в класс? удобство лишь в том, что не будем передавать объект класса? или как мы будем хранить библиотеку интерфейсов, если у них есть общие цели, раз мы не хотим выносить их в общий класс предок? не порождает ли это тех же проблем, только красивый вызов псевдонепроцедурный?
может запутанно сказал, но давайте предположим, что нам всё таки понадобилось вынести десяток важных общих функций в один класс предок. Да вся детвора будет обращаться к этим функциям. Утверждаем что это некрасиво и лучше создадим десяток интерфейсов и каждый из них интегрируем в каждый класс потомка. Да? Это и есть решение озвученной проблемы?
8:33 *Как ты угадал, волшебник!?*
**плачет**
"Реальный мир полон наследования..."
Что-то я не встречал в реальном мире абстрактных капибар )))
Видимо подразумевается, что в реальном мире условные автомобили А2 автоматически меняют колеса когда в чертеж базовой модели А1 внесли изменение. Или когда родители научились кататься на лыжах то все их текущие и будущие дети тоже умеют кататься на лыжах. Наследование по всюду, если присмотрется
@@ievgenk.8991 У меня нет такой оптики, к сожалению, чтобы так присмотреться ))
@@vitiok78 Видимо такую оптику выдают только самым трушным и преданным адептам ООП)
@@ievgenk.8991 Они её автоматически наследуют же-ж
ты и реальных не встречал скорее всего. но свойства её описать можешь. магия
В чём проблема использовать интерфейсы, а не классы? Тогда мы избавляемся от проблем наследования, и используем полиморфизм. Если я не прав, то поправьте
Так интерфейс это тот же класс, только абстрактный
@@denys.martyniuk не соглашусь
Интерфейсы декларируют ожидаемое поведения класса, ряд проблем наследования они не решают, например, интерфейс не хранит общий код (с этим в java можно поспорить, потому что есть default методы, но и с ними не всё гладко), интерфейс не позволяет объявить не публичные методы (например, шаблон "песочница" построен как раз на protected методах), интерфейс не позволяет объявить состояние объекта (если мы хотим расширить поведение и состояние какого-то объекта, не изменяя при этом класс объекта, то интерфейс нам не поможет). Есть мнение, что наследование лучше всего использовать не в глубину, а в ширину
@@denys.martyniuk абстрактный класс, это абстрактный класс, а интерфейс это что-то на подобии класса, сущность в которую можно написать только методы и то без их реализации. В PHP еще есть трейты, вот ими можно расширить класс и без наследования просто подключив трейт там где надо
@@alexanderchudaev9431 в С++ нету ключевого слова интерфейс, там интерфейс это чисто абстрактный класс, от этого отталкиваюсь
Привет!
почему Вами не назван четвертый принцип ООП " Абстракция" ?
Наследование - инструмент, который вполне можно использовать иногда. Хейтить его не надо, его надо просто уметь готовить и не пихать везде, где нужно и не нужно. Но я не согласен, что его надо возводить в какой-то абсолют и с ироничной улыбкой в снисходительном тоне говорить о тех, кому не нравится наследование, и кто использует его крайне редко и только там, где оно действительно эффективней всех других методов.
ad-hoc полиморфизм в каком-то виде без наследования возможен, когда язык позволяет вручную заменять ссылку на метод, например