Pull to refresh

Comments 122

Я думаю всем уже окончательно заморочили голову бесконечные статьи про полиморфизм, но меня они достали тоже и я решил… написать свою.

Ха-ха… так-то правильная реакция)))
Это тепер, что и мне писать?:)
Да, теперь каждый не довольный должен написать статью про полиформизм, добавить немного о наследственности и пообещать написать про инкапсуляцию))
Это типа обряд посвящения будет в ООП? Или таинство ООП?
Настоящее таинство ООП познал только тот, кто написал статью про полиморфизм на Хабре…
Нее… теперь каждый должен наследовать свою статью от данной и переопределить некоторые виртуальные абзацы в ней :)
В конце мы увидим такое дерево наследования!
Вобщем — программный продукт, который пишется краудсорсингом :)
5 баллов! Вам нужно книгу написать, для начинающих.
Согласен с некоторыми мыслями в статье, но персонально для изучения какой-либо концепции я всё же предпочту сжатое изложение с небольшими вставками кода — такой текст я смогу спокойно охватить целиком и поразмышлять над ним. Витиеватый же текст с отступлениями от темы, возможно, легче прочитать, но (мне) гораздо труднее воспринять. Кроме того, вы предлагаете свои аналогии — человечки, резюме, станки… А эти аналогии совсем не близки мне, и каждый раз приходится задумываться, что же имеется в виду. Аналогичные неудобные для себя моменты я замечал и при чтении некоторых книжек (при рассказе про базовые абстрактные концепции или, например, про паттерны программирования, часто прибегают к аналогиям). Не знаю уж, моя личная ли это особенность, или что-то общее.
Это общая проблема. И, собственно, поэтому-то и обучение о мастера гораздо быстрее приводит к успеху, чем чтение книг. Просто потому что автор книги не может знать о том, какими знаниями читатель уже располагает. Это можно только предполагать. Программисту с опытом проще понять небольшую программу на известном ему языке программирования чем более длинное объяснение на человеческом языке. Но объяснять программисту на PHP базовые понятия ООП на примере языков Scheme и C++ — это не лезет уже ни в какие ворота.
Ну, причём там был Scheme я тоже не понял :)
Но про C++ не совсем согласен. Вообще, если писать что-то именно для людей, знающих или предпочитающих только PHP, то код логично будет приводить на PHP. Но когда речь идёт о базовых концепциях, обычно всё же обращаются к «академическим» си-подобным языкам, думаю, просто потому, что о них имеет представление наибольшее число читателей. Поэтому, если не фокусироваться на аудитории PHP-кодеров, я бы выбрал как раз C++.
С++ — это ужасный «промышленный» язык. В нём куча заморочек, которые к базовым концепциям не имеют отношения и только запутывают начинающим. В частности ни в одном вменяемом ООП-языке даже и понятия-то «виртуальная функция» нет. Ибо нет функций невиртуальных. Использовать его для базовых концепций — всё равно что рассказывать старенькой бабушке о том как отличить грипп от обычной простуды на латыни. Для специалистов — это, может быть, и хороший язык — но никак не для начинающих!
Согласен, к тому же ООП в С\C++ притянуто за уши.
да что вы говорите? :) А, по моему, уж куда как интереснее всяких яв и шарпов.
Что интереснее? С++ не Native-OOP язык.
C вообще функциональный язык
C++ можно назвать языком ООП с большими оговорками, т.к. многие концепции ОПП там нарушены.
К примеру в фундаментальном понятии ООП нету множественного наследования, точнее есть но оно делается через интерфейсы, а в C++ один класс может наследовать сразу несколько классов, и при совпадение некоторых сигнатур у программиста может медленно съехать крыша :))
С точки зрения ООП .net и java максимально реализуют эту концепцию
www.ctc.msiu.ru/materials/Book/node82.html
и там есть такой вот пункт

Определение 1.3 Наследование (inheritance) — свойство объектов, посредством которого экземпляры класса получают доступ к данным и методам [классов]-предков без их повторного определения.

Вроде я правильно прочитал множественное число.
Возможно этот источник не заслуживает доверия, тогда приведите линк на вашу «концепцию ООП»
Да и как-то не находится информация о едином наследнике. В википедиях ( ru.wikipedia.org/wiki/Наследование_(программирование) ) о наследовании пишут о всяком, и делают оговорку, что в наслениках С++ (ява, шарпы) решено было отказаться от множественного, но никаких упоминаний о концепциях там нет.
Множественное число здесь уместно не по причине множественного наследования [A, B, C] -> D, а по причине A -> B -> C -> D. Класс D будет иметь доступ к данным и методам своих предков (C -> B -> A).

А выбор между множественным наследованием и интерфейсами — воля разработчика. Или архитектора ;-)
Процедурный! Функциональная парадигма — это совершенно другое!
C вообще функциональный язык

Процедурный, на самом деле. К функциональным языкам он особого отношения не имеет.
Я бы согласился с примером про латынь, если бы большинство бабушек понимало латынь. Я же говорю только про то, что С++ поймёт наибольшее число читателей, а недостатки его я тоже покритиковать могу :) Но не думаю, что они являются существенной проблемой для объяснения базовых концепций и т.п. В частности, я не вижу особой проблемы с виртуальными/невиртуальными функциями, и понимание отличия между ними на мой взгляд весьма полезно — то, что в других языках нет невиртуальных функций ведь не означает, что не нужно объяснять, что будет происходить с функциями при наследовании.
Вы удивитесь, но программистов понимающих многие тонкости C++ куда меньше чем бабушек понимающих латынь. В частности людей поинмающих что такое виртуальные функции — очень и очень мало.

Это в силу возраста. Те кто учил С/С++ раньше чем новомодные языки, понимают тонкости очень хорошо. Кстати, именно это им помогает разбираться со многими тонкостями в других ОО языках.
И не так их и мало, как Вы говорите. Можно, кстати, посмотреть опросы по владению языками, по моему, на хабре были.
Те кто учил С/С++ раньше чем новомодные языки, понимают тонкости очень хорошо.
Ой ли? Я знаю кучу вопросов на которые только один человек (из сотен «C++ гуру» приходивших к нам на работу) смог дать правильный ответ — и то только потому, что он до этого занимался разработкой компилятора C++. Даже на многие простейшие вопросы, касающиеся виртуального наследования люди ответить не могут. Про STL (а он вроде как часть языка — во всяком случае описание C++ его включает) я вообще молчу.

Я, например, знаю только то, что использую — остальное из головы выветривается… Про виртуальное наследование сейчас бы тоже, скорее всего не ответил, хоть и разбирался с этим (интересно, зачем?). Но, честно говоря, я вообще не понимаю почему вообще речь пошла о тонкостях языка, хотя разговор был совсем не об этом.
Виртуальное наследование и в С# присутствует (если я правильно понял о чем идет речь, конечно). Я пытался убрать дублирование кода таким образом. Вообще, этот момент не кажется мне очень сложным.
Я не знаю про C#, но в C++ виртуальное наследование используется для разрешения проблем со множественным наследованием. Получается такая подпорка, которая считается многими ошибкой проектирования, и обычно не используется ввиду этого. Ничего сложного нет, но (мне) просто не нужно, вот и забывается.
Думаю что неправильно. Простой вопрос на виртуальное наследование — что выведет эта программа:

struct V { V() { std:: cout << «V is created!» << std:: endl; } };
struct A: virtual V { A() { std:: cout << «A is created!» << std:: endl; } };
struct B: V { B() { std:: cout << «B is created!» << std:: endl; } };
struct C: virtual V { C() { std:: cout << «C is created!» << std:: endl; } };
struct D: A, B, C { D() { std:: cout << «D is created!» << std:: endl; } };

int main(void) {
D d;
return 0;
}

Не такой большой процент программистов на C++ могут сходу ответить на этот вопрос.
Я бы предположил что:
D is created!
A is created!
B is created!
C is created!

А в С# конечно этого нет, поскольку нет множественного наследования.
С предположением я, конечно, не прав! Какой конфуз;)
Хороший пример на понимание того, что такое DoD (Diamond of Death) и того, как от него уйти.

Так как A и C — виртуально наследуются от V — для них будет создан один объект V.
В случае с B — нет виртуальности наследования, поэтому будет создан ещё один объект V.

То есть получается:
V is created!
A is created!
V is created!
B is created!
C is created!
D is created!
STL кстати не мудрено забыть:) STL была включена в спецификацию позже. И выглядит он, как полумера. Да и уж если шутить, то трудно понять то, что придумал русский.
Согласен, что не обладая первоначальной подготовкой и опытом трудно разобраться с тонкостями, но так в любом деле, Вам не кажется?
STL была включена в спецификацию позже.
Это было больше 10 лет назад!

Согласен, что не обладая первоначальной подготовкой и опытом трудно разобраться с тонкостями, но так в любом деле, Вам не кажется?
Да, но тут вроде речь идёт об использовании C++ для обучения новичков. Не стоит ли для этого выбрать язык в котором будет не так много тонкостей?
«Это в силу возраста. Те кто учил С/С++ раньше чем новомодные языки, понимают тонкости очень хорошо. Кстати, именно это им помогает разбираться со многими тонкостями в других ОО языках.»

Поверьте знания hardcore C++ вам только очень сильно помешают при изучении Java или c#. А если захотите еще и сертифицироваться в Sun Certified Java Programmer или майкрософтовский тест(не знаю как называется не сдавал), то там есть специально много вопросов на людей которые очень хорошо знают C++ но не очень много времени потратили на изучение Java или C#
То, что в других языках нет невиртуальных функций ведь не означает, что не нужно объяснять, что будет происходить с функциями при наследовании.
Не означает. Это просто означает, что в других языках нет этой идиотской ошибки проектирования, допущенной создателями C++. Скажем невиртуальные функции в Java на самом деле есть — только там нет с ними проблем: они описываются ключевым словом final, не перекрываются и не таят в себе никаких подвохов. И знать о том, что плохо спроектированных языках есть такое понятие как виртуальная функция не более полезно, чем умение склонять слова на латыни для людей, латынью не пользующихся.
Недостатки в С++ во многом растут из-за непонимания его новичками, которые потом принимаются его хаять.
Реальные недостатки языка аргументированно смогут описать и дискутировать очень мало человек, остальные же пустозвоны, которым не хватило ума осознать язык, которые переливают из пустого в порожнее свои личные неудачи.
вот!
наконец-то здравый смысл восторжествовал =)
Извините, но все эти «станки», «школьники» и прочее в тысячу раз менее наглядны, чем 10 строк на псевдокоде. Я уже молчу о том, что «все аналогии ложны», обладают для каждого своим собственным смыслом и т.д.
«все аналогии ложны»
Точно, есть даже специальное слово — субъективны.
Это всё сугубо индивидуально.
Мне понравилась статья тем, что какой-то нестандартный подход в обьяснении использовался.
Автору спасибо, у Вас приятный слог и хорошие навыки обучения :)
Теперь ещё нужен будет цикл статей: «50 фактов о полиморфизме», «Всё, что вы хотели знать о полиморфизме, но боялись спросить» и, кульминация серии, «Как правильно воспитать в своём коде полиморфизм» :-)
А сравнения интересные, спасибо.
Главное что я хотел показать это что «объекто-ориентированное программирование» != «программирование на объектно-ориентированном языке». Можно писать объекто-ориентированные программы и на фортране и даже в машинных когдах. А можно — необъектно-ориентированные на самом что ни на есть суперобъектном смоллтолке…

Прежде всего есть объектно-ориентированный подход, а уже во вторую очередь — языки программирования. Ядро Linux'а написано на смеси C и ассемблера, что не мешает ему быть примером неплохо спроектированной объектно-ориентированной системы, а во многих JS-поделках объектно-оиентированным программированием и не пахнет несмотря на его поддержку в языке.
И в заключение: Есть ли жизнь после полиморфизма?:)
Для «простого» объяснения слишком много букв.

Если хотите объяснить просто, ссылайтесь на официальное определение полиморфизма. Химический элемент углерод называют полиморфным, поскольку он встречается в нескольких формах — древесный уголь, грифель, алмаз. Таким же свойством обладают сера и кислород. А вот гелий, например — нет.

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

Строго полиморфизм определяется через множества/подмножества. Это для любителей математики. :)
Для «простого» объяснения слишком много букв.
Простое объяснение — это объяснение, понятное максимальному кругу людей, а не максимально короткое. Вы своему ребёнку тоже будете рассказывать о том почему небо голубое вывалив на него кучку формул из курса физики?
А почему небо голубое?:)
Вы подкалываете или действительно не знаете? Задайте вопрос Гуглу, он ответит. Для ребёнка, наверняка, ответ придётся либо удлинить ответ ещё раз этак в пять, либо наплести какой-нибудь чуши. А ответ с формулами уложится в 3-4 строки, но будет ли он проще?
Клёва :) Что бы там не говорили, а стиль изложения мне понравился. С долей юмора и, в общем, всё по делу.
Написано конечно прикольно. Особенно когда сам знаешь ООП. Но вот понял бы о чем идет речь фортранщик? Я не уверен.
Покажите хоть одного живого:)
Спасибо. Буду показывать статью тем, кто хотел бы узнать о программировании но боялся спросить. Например, девушкам, которые по воле судьбы вынуждены слушать весь этот программерский трёп про наследование, инкапсуляцию и полиморфизм, но вроде как это им и не надо знать, а понять хочется.
(Всё выше я сказал совершенно серьезно, без стёба)
Почему то подумал, что речь в посте о полиморфизме пойдёт.
Хочу тоже самое, но в картинках и в виде комикса. С лисами, волшебниками, жирафами и роботами.
Хочу тоже самое, но с блэкджеком и шлюхами… А вообще, к чёрту тоже самое.
Вот «тоже самое, но с блекджеком и шлюхами» — это наследование и есть.
Что может быть проще: вы построили точно такое же казино, но с блекджеком и шлюхами — это наследование.
Шлюхи в этом казино разные: блондинки, брюнетки, рыжие, но «интерфейс» у них одинаковый, а результат обслуживания определяется их опытом и личными качествами — это полиморфизм.
:)
Наконец-то я разобрался, что такое наследование и полиморфизм. Оказывается, все очень просто. Спасибо.
Вот это статья заслужывает ++ в карму не использованно не одного программного кода(фортран 66) и разжована каждая буковка)) Спасибо за статью. А вы случаем не где не преподаёте?
Сейчас — нет, но да, опыт имеется…
Просто действительно я еще не встречал такие простые в понимании но объемные статьи не использовав ни строки кода и не прибегая к какому либо языку в качестве примера. Статья класс, всё разжовано. Спасибо.
Не за что. На самом деле прозвучавшие у многих замечания о том, что примеры на известном читателю языке облегчили бы понимание справедливы — но они исходят из предположения о том, что человек знает какой-то язык программирования и знает хорошо. В этом случае соглашусь — да, примеры на этом языке облегчают понимание, но… всё описание становится не очень-то нужным: сейчас сложно найти язык, который бы не включал в себя объектно-ориентированное программирование в том или ином виде, так что если человек неплохо знает, скажем, PHP (или, тем более, C++) — то уж азы-то объектно-ориентированного программирования он знать обязан. Зачем ему подобная статья?
Ну я РНР программист, всё-ж с увлечением прочитал))
Интересно, а для статьи определён оператор «++» =)
автор зануда, да и не только он — все кто пытается поправлять и говорить — ты не правильно толкуешь, дай я скажу
чем ссылаться на кого-то и говорить «ща я вам понятнее объясню» не лучше ли написать самому и от себя.
нехорошо самооценку поднимать опуская других

по тексту могу сказать что объяснил автор сумбурно, если бы я не знал понятия возможно бы не понял.
например кусок про виртуальные функции лично меня только запутал
UFO just landed and posted this here
Виртуальные функции — это не концепция. Это особенности реализации. Потому и сложно объяснить — какой в них смысл. Придумаете более понятное объяснение — напишите в отдельном топике. Можно будет обсудить.
UFO just landed and posted this here
Что такое объект в ООП? Это способ, позволяющий описывать большие программы.

Я всегда считал, что объект в ООП — это некий контейнер для хранения данных и методов для работы с этими данными ;)
Исправил. На самом деле во многих языках методы для работы с данными не входят в объект CL, Forth, etc…
> На самом деле во многих языках методы для работы с данными не входят в объект
Спасибо, не знал!
На самом деле это как раз логично. Представьте себе что у вас есть программа с двумя геометрическими фигурами (круг, квадрат) и двумя устройствами вывода (принтер, экран). Куда отнести четыре функции вывода этих объектов? Обычно заводят пару в круге и пару в квадрате. Но концептуально это один метод! Он должен «висеть» где-то между объектом-геометрической фигурой и объектом, где эту геометрическую фигуру можно изобразить и выбирать одну из четырёх доступных реализаций в зависимости от типа объекта. Так сделано в CL и даже в STL (какой-нибудь std:: find), но концептуально эта конструкция во-первых дальше от антропоморфного принципа и потому хуже помещается в голове, а во-вторых — менее эффективна…
Как-то очень хочется снять зависимость таких объектов и убрать дублирование:)
Желание здравое (дублирование — зло и от него по возможности нужно избавляться), но не всегда реализуемое. Более близкий вам пример — это Object.Equals. Сколько приёмов придумано для того, чтобы не вводить нестандартные отношения эквивалентности (например приравнивающие ваш класс MySuperInt к Int). А почему? А потому что перекрыть Equals для целых чисел нельзя. Если вы подумаете то найдёте, я уверен, десятки примеров в которых метод хочется сделать именно мультивиртуальным — но нельзя. Потому что определение класса закрыто и переопределению не подлежит. Иногда хочется добавить поля данных «на лету» (в CL это, кстати, тоже возможно), но гораздо чаще — те или иные методы.
Потому что определение класса закрыто и переопределению не подлежит. Иногда хочется добавить поля данных «на лету» (в CL это, кстати, тоже возможно), но гораздо чаще — те или иные методы.


В своем языке я применил (или придумал?) механизм расширений, который позволяет добавлять (и переопределять) методы внешних классов, без наследования. Несмотря на нападки ортодоксов, могу отметить, что использование расширений (по крайней мере в нашем проекте) очень удобно и здорово облегчает жизнь.

В качестве банального примера:
extend int {
    public const function times(block b) {
        for (var n = 0; n < this; n++)
            b(n);
    }
}
 
export function main() {
    3.times() { |i| puts(i as string); };
}


Если кто не понял: в рамках текущего модуля мы расширяем класс класс int, объявленный в стандартной библиотеке, методом times() который выполняет некоторый код N раз. Далее этот метод вызывается у экземпляра класса, в данном случае числовой константы 3.

P. S.: Наиболее эффективен этот подход при работе со сторонними модулями.

Как правило, существует некоторый интерфейс модуля, методы которого оперируют с объектами — инстанциями классов объявляемых в рамках этого стороннего модуля. Соответственно, изменять их напрямую мы не имеем возможности. Иногда бывает потребность дополнить такие классы своими методами для удобства обработки или взаимодействия. Проблема заключается в том, что в традиционных языках (С++), для этого потребовалось бы писать классы обертки, реализующие нужные методы и далее оборачивать весь интерфейс так, чтобы он работал с обертками. Естественно, это не относится к полностью динамическим языкам, где это можно сделать.

В случае К++, достаточно расширить класс своими методами и можно тут же применять их, что называется, на практике :)

P. P. S.: Интересно мнение товарища khim :)
1. Сложно судить без подробного рассмотрения байткода «gide» и реализации этих расширений.
2. Та же проблема что с С++: получаем такую несколько «помесь жабы с мотоциклом», хотя на практике работать должно
3. Ваш язык — как вам удобно, так и делайте

Немного по поводу 2: в большинстве распространённых языков где к «описанию класса» можно добавить свои методы описание класса их не содержит! Скажем в LISP-подобных языках у вас будет (eqv? i 1) или (eqv? I i) — и будет использоваться самый подходящий метод: если I имеет тип MySuperInt — то метод, если MySuperDBInt — другой. То есть метод существует как бы «снаружи» и никого не удивляет что его можно расширить (опять-таки сравните с перегрузкой операций в C++ по синтаксису). Наоборот вызов через.имя предполагает что описание метода «живёт» внутри класса и изменить его нельзя — хотя в языках типа JavaScript'а и возможно. Опять-таки посмотрите на JavaScript-библиотеки и C++ библиотеки: объективно потребность в такой операции есть, если язык её поддерживает, то используют её «и в хвост и в гриву», если нет — то используются разные хаки (обёртки — ещё не самое страшное) ибо потребность никуда не исчезает…

P.S. Я так понимаю до мультивиртуальности вы не дошли? Могу ли я реализовать исходный пример (два класса геометрических фигур, два класса их принимающих объектов, всё это нужно как-то связать). Хороший пример — библиотека типа Cairo, где у вас есть куча примитивов и более десятка backend'ов. Разумеется для каждой пары «backend<->primitive» нужна своя функция и, разумеется, программа, использующая Cairo об этом догадываться не должна (для того Cairo и существует), но внутри самой Cairo как быть? Сейчас — там изрядная каша, если бы язык поддерживал недорогие мультивиртуальные методы — было бы гораздо проще…
Респект и уважуха!!!
Лично я считаю один из лучших способов объяснять таким путём. Причём правильно заметил, что людям свойственно всё сравнивать именно с повседневными и простыми вещами, так лучше запоминается и легче усваивается, особенно когда очень много всего.

Любое «простое объяснение» длиннее, чем цитата на баше — это сложное объяснение.
Если бы я пытался понять хоть какие-нибудь принципы ООП, начав с этой заметки, то абсолютно ничего бы не понял.
Уж слишком многословно и слишком абстрактно. До такой степени абстрактно, что трудно ассоциировать с программированием =)
Простые объяснения не должны быть абстрактными, они должны быть на пальцах.
в корне не согласил с вами
так говорят люди которые знают что какое ООП и профессионально им владеют
я уверен на 100% что если взять 100 человек которые изучили основы программирования и начать их тыкать заумными словами время на понимание возьмёт в 3 больше чем вот таким образом

Говорю это не просто так а исходя из того что я вижу в высших преподавательских заведениях в одном из которых я учусь и пытаюсь объяснить сокурсникам, что имели ввиду на лекциях эти профессора
UFO just landed and posted this here
Объяснение завязано на каких-то особенностях C++, в которых я не разбираюсь
Угу. Про виртуальные функции (вернее про невиртуальные функции) сложно объяснить понятно ибо никакой простой концепции за ними не стоит. Это просто оптимизация — но с тяжёлыми последствиями. Ведь как реализуется полиморфизм? Где-то внутри объекта написано что именно нужно следать когда его попросят напечатать «Hello, world!». И компьютер каждый раз при вызове метода должен заглядывать «внутрь» и решать — то ли ему «писать ручкой по бумаге», то ли «долбить долотом камень». И это — операция не мгновенная. Но во многих случаях нам не нужно этого делать! Скажем если у нас есть ровно один объект соответствующий определённому резюме, никаких альтернативных (полиморфных) реализаций нет, то мы точно знаем — будем ли мы писать по бумаге или будем долбить камень. Это — подход Java/C#/etc. Простой, логичный, бесхитростный. Описал метод как финальный — всё, никаких вариантов! Никакого полиморфизма. Зато быстро. Ибо выбора-то нет и связанных с ним метаний — нету тоже. В случае же с C++/Delphi/etc используется хитрая композиция: варианты могут быть, но если мы точно знаем тип объекта — то мы можем обойти все эти изыски. А если не знаем — то получаем большой облом. Зачем так сделано? Правильный ответ: по историческим причинам. Нет в этом никакого смысла. На каждую программу где это применено по делу найдётся тысяча других программ, где эта возможность привела к сбоям, проблемам и многим часам отладки. Но так сделано — потому приходится с этим делом мириться…
UFO just landed and posted this here
для программирования на пальцах это объяснение может и пойдёт, но большинство людей программирует всё же на компьютерах.

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

наконец, Luca Cardelli написал прекрасную статью On Understanding Types, Data Abstraction, and Polymorphism. Рекомендую прочитать и осознать её, прежде чем учить кого-либо тому, что такое полиморфизм.
Luca Cardelli написал прекрасную статью On Understanding Types, Data Abstraction, and Polymorphism. Рекомендую прочитать и осознать её, прежде чем учить кого-либо тому, что такое полиморфизм.
Великолепная статья! Гениальная! Стиль Бурбаки — именно тот стиль, после которого на вопрос «сколько будет два плюс три» ребёнок даёт ответ «два плюс три будет столько же, сколько три плюс два, потому что сложение коммутативно»…

Перед тем, как заниматься расщеплением волос, описанным в подобных «фундаментальных трудах» нужно понять что такое полиморфизм «на пальцах». А потом уже задуматься над вопросом «а может ли использоваться один и тот же код для обработки разных типов» (universal polymorphism — см. CLOS, generics в Java5 и т.п.) наряду с обычным, естественным полиморфизмом (ad-hoc polymorphism — C++, PHP, etc) и как ещё можно это можно сделать (coercion polymorphism, например).

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

На мой взгляд объяснение полиморфизма следует начинать как раз с функций ведущих себя одинаково на значениях разного типа (операции со списками, например).
На мой взгляд объяснение полиморфизма следует начинать как раз с функций ведущих себя одинаково на значениях разного типа (операции со списками, например).
Ни в коем случае! Для того, чтобы человек мог поверить в то, что пара совершенно обычных функций
struct kobject * kobject_get(struct kobject * kobj)
{
  struct kobject * ret = kobj;

  if (kobj) {
    WARN_ON(! atomic_read(&kobj->refcount));
    atomic_inc(&kobj->refcount);
  } else
    ret = NULL;
  return ret;
}
void kobject_put(struct kobject * kobj)
{
  if (atomic_dec_and_test(&kobj->refcount))
    kobject_cleanup(kobj);
}
вообще имеет хоть какое-то отношение к полиморфизму нужно не только знать что такое полифорфизм, но и обладать недюженным математическим воображением. Потому что эти функции, в конечном итоге, слабо отличаются от совершенно обычных, прозаических функций типа вычисления факториала. Полиморфны они только в нашем воображении… и в программе во время исполнения, конечно…

Не вводите рациональные числа как множества эквивалентных дробей, прошу вас — лучше расскажите про то как резать яблоки…
бггг, поверьте мне, я не собираюсь делать это на примере исходников ядра Linux.
Ну зачем же так про Бурбаки, отличная подборка материала для знающих толк :)
Великолепная статья! Гениальная! Стиль Бурбаки — именно тот стиль, после которого на вопрос «сколько будет два плюс три» ребёнок даёт ответ «два плюс три будет столько же, сколько три плюс два, потому что сложение коммутативно»…


Я не призываю вас учить по ней детей. Даже и не надо пытаться рассматривать людей изучающих программирования подобным образом, сколько бы им лет не было…

Если вы думаете, что ребёнок поймёт что такое полиморфизм после того, как прочитает взрывающую мозг фразу
Этот принцип просто-напросто гласит что резюме работника не совпадает с самим работником.

то вы сильно заблуждаетесь. Эта фраза построенна весьма характерным образом и выдаёт наличие у вас научно-технической подготовки, но для обучения она неподходит.
Если вы думаете, что ребёнок поймёт что такое полиморфизм после того, как прочитает взрывающую мозг фразу.
Не поймёт. Я хотя и с сарказмом, но довольно-таки чётко определил аудиторию: программист не знающему ни одного, известного вам, языка программирования (ну например он всю жизнь использовал только FORTRAN-66). У детей большие проблемы с воспринятием таких абстрактных концепций как процедура и функция. Где-то до 11-12 лет их этому всему учить бесполезно (за редким исключением). И уж полиморфизму — тем более.

Эта фраза построенна весьма характерным образом и выдаёт наличие у вас научно-технической подготовки, но для обучения она неподходит.
Почему нет? Предложите что-нибудь получше. IMNSHO если человек не может воспринять подобную концепцию, то объяснять ему что такое полиморфизм либо рано, либо просто не нужно.
Потому что для любого человека эта фраза тавтология. Потому трудно представить себе человека, который скажет фразу «резюме совпадает с человеком» и даже представить это себе трудно.

Программисту знающему один язык (кстати это должен быть язык, в котором полиморфизм «не в ходу»), совсем не трудно осознать примитивные концепции любого другого языка (если это не восьмидесятилетняя бабушка со спекшимися мозгами, которая когда-то 60 назад втыкала штекеры в гнёзда и называла это программированием).
Программисту знающему один язык, легко прочитать статью Карделли и осознать полиморфизм. Ну а после перейти к конкретным примерам, для тех языков, которые он знает или собирается изучать.
Потому что для любого человека эта фраза тавтология. Потому трудно представить себе человека, который скажет фразу «резюме совпадает с человеком» и даже представить это себе трудно.
И именно поэтому она там и появилась. В чём проблема? Изначально полиморфизм — это разделение одного интерфейса и множества реализаций. Что пример с человеком и резюме иллюстрирует как нельзя лучше. Потом можно заметить что есть другие похожие вещи — и их тоже назвать полиморфизмом (людям, знакомым с generics в Java5 или C# будет легче, чем другим). Но если с этого начать… ой плохо будет…

Программисту знающему один язык, легко прочитать статью Карделли и осознать полиморфизм.
Если бы. Математику не знающему ни одного языка легко прочитать статью Карделли и осознать что такое полиморфизм. Программисты же… Думаю 5 из 10 знающих что такое полиморфизм прочитав статью Карделли скажут «ну и бред же там написан», а 9 из 10 не знающих что такое полиморфизм не только ничего не поймут, но будут твёрдо уверены в том, что Карделли чего-то явно не того выкурил…
Хорошая статья. Вот я, помню, тоже мог доказать в своё время, что Pascal всё-таки круче C :))
земля ему пухом, очень хороший был язык, мне очень нравился :)
да и сейчас жив и здравствует, на FreePascal посмотрите…
FORTRAN-66
У меня мама на таком программировала =)
Спасибо за статью, вот такими словами нужно учить студентов первого курса.
Все опять слилось в языковой холивар. Есть одна концепция, которую я знаю лучше всего. Программист всегда противится и критикует решения, которые не понимает и не хочет в них разбираться. Более того, для людей, которые сменили несколько языков, с каждым новым открывается его понимание и со временем влюбленность в него. А плюсы старых, со временем забываются.
Хороший разработчик — умеющий выбрать язык и среду для РЕШЕНИЯ ЗАДАЧИ. Ключевое слово, не язык, не среда, а именно решение задачи.
Обидно, что C++ очень критикуют, с ненавистью прям. Конечно он не без костылей, но жизнь спасал часто ;). Я люблю любой язык, который помогает мне решать задачи ;).
И еще одно, хорошие или плохие C/C++ знать вам рано или поздно придется, по вполне объективным причинам ;).
Программист всегда противится и критикует решения, которые не понимает и не хочет в них разбираться. Более того, для людтей, которые сменили несколько языков, с каждым новым открывается его понимание и со временем влюбленность в него.
Ой ли? Не cудите обо всех по себе. К людям, которые «Пастернака не читали, но хотят сказать...» иначе как со смехом относиться нельзя. Но почему вы считате что все кто критикуют C++ — его не знают?

И еще одно, хорошие или плохие C/C++ знать вам рано или поздно придется, по вполне объективным причинам ;).
Ну если «он очень разрекрамирован и моден» считать объективными причинами, то да. Скажу больше: за последние несколько лет мне с C++ пришлось общаться больше, чем с каким-либо другим языком. Но полюбить язык в котором если нужно удалить гланды, то это делается строго через задний проход я не могу.
Вы не правильно поняли меня. Те, кто критикуют хорошо ознакомившись это одно (таких здесь меньшенство). Я же говорил, про людей, которые критикуют новое, потому что либо не понимают, либо не хотят разбираться. Это как раз ваш пример про Пастернака ;). Таких людей очень много, более того, такое поведение присуще всем, даже хорошим разработчикам в той или иной мере.
Объективные причины заключается не в моде и рекламе ;). На этих языках реализовано больше всего библиотек и операционные системы. Большинство интерфесов на границе библиотек являются сишными. От сюда вывод (который лично для себя я сделал на практике) рано или поздно решая задачу вы упретесь в лучшем случае в интерфес, а то и в реализацию, которая будет на C/C++.
Повторюсь, у C++ много плюсов и много минусов, спорить на эту тему не буду, мое отношение к этому языку все равно крайне позитивное.
> На этих языках реализовано больше всего библиотек и операционные системы. Большинство интерфесов на границе библиотек являются сишными.

А почему так произошло — Вы не задумывались? И почему Вы уверены, что такая ситуация сохранится в обозримом будущем?
Тут трудно сказать, наверное множество причин. Одна из них — производительность, множество ограничений этих языков связано именно с ней. По поводу того, что ситуация сохранится, хочу, но не уверен. Достаточно оптимистично надеяться, что такое наследие кода будет переписано.
Вы совершаете обычную ошибку, привязывая понятие полиморфизма к ООП. Почитайте хотя бы английскую википедию. Там вы увидите, что определение полиморфизма очень простое: это свойство языка программирования, при котором один и тот же кусок кода может быть выполнен для переменных разных типов. Классический пример не объектно-ориентированного полиморфизма, например, в C++ — шаблоны и перегрузка функций (и операторов).
Вы совершаете обычную ошибку, привязывая понятие полиморфизма к ООП.
Это не ошибка. Я потому и говорю о полиморфизме раньше наследования, что для него не требуется классических классов/объектьов или чего-нибудь в этом роде и даже наследование.

Классический пример не объектно-ориентированного полиморфизма, например, в C++ — шаблоны и перегрузка функций (и операторов).
И? Где это вступает в противоречие с тем, что мною описано? Перегрузка функция — весьма ограниченный полиморфизм времени компиляции, шаблоны — классический пример полиморфизма без явного выписывания «резюме». И как раз-таки эти механизмы — отличная иллюстрация к убогости C++: перегрузка функций и шаблоны в C++ — отлично реализуют ООП… если вы не используете наследование и виртуальные функции. Как только вы их смешиваете с шаблонами — всё, проще застрелиться. Недаром не только STL, то и большинство других «шаблонных» библиотек не используют «классический C++ ООП» с виртуальными функциями. Генерики (как в Java или C#) не столь гибки, как шаблоны, но зато гораздо проще в использовании — и решают 99% практических проблем.

P.S. Вы когда детям про умножение рассказываете тоже его вводите как «операцию, образующую коммутативную группу для всех ненулевых элементов множества, cвязанную со сложением через закон дистрибутивности»? Если да — то мне их жаль…
Я думал, здесь не дети собрались. Если я ошибаюсь в этом, то вы конечно правы. А если здесь взрослые думающие люди, то рассказ о полиморфизме в обязательном контексте ООП вводит их в заблуждение.

Что касается C++ — вы неправильно понимаете его философию. Шаблоны дают высокую эффективность в сочетании с невероятной гибкостью и контролем типов времени компиляции. Именно ради этой высокоуровневой эффективности C++ до сих пор жив, и не собирается уступать место С#/Java, у которых совсем другие задачи. Вместе с тем шаблоны прекрасно дополняют механизмы наследования и виртуальных функций, как это лет 9 назад описал в своей замечательной книге Александреску. Если вам не нужна эффективность, или просто не хватает… чего-то… чтобы разобраться в сложности C++ — это не повод говорить об его убогости.

И не надо так агрессивно реагировать на критику, что вы все здесь на хабре какие-то неуверенные в себе, как подростки. Как напишу что-нибудь кроме хвалебного отзыва, сразу карма вниз летит. Мне наплевать на нее конечно, но как-то несерьезно это.
Я думал, здесь не дети собрались.
А что — в полночь когда человеку исполняется 18 в нём переключается рубильник и он становится сразу другим человеком?

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

Что касается C++ — вы неправильно понимаете его философию.
У C++ нет философии. Просто нет. Филосьфия есть у его отдельных компонент, но единой, целостной философии у этого языка нет — отсюда все беды.

Если вам не нужна эффективность, или просто не хватает… чего-то… чтобы разобраться в сложности C++ — это не повод говорить об его убогости.
Я боюсь это вам не хватает… чего-то… чтобы критически взглянуть на своего кумира. Хотя бы один пример: какой глубокий смысл в невиртуальных функциях классов? Скажете «эффективность»? Чёрта с два: final методы Java в этом смысле ничуть ни хуже — а подвохов таят несравненно меньше. Или другой: какой смысл в конструировании объектов по частям (которое не позволяет использовать виртуальные функции в конструкторе как виртуальные)? Это приводит к лишнему коду, замедлению (небольшому, впрочем) и вынуждает изобретать кучу дурацких приёмов (в частности наиболее распространённый подход — ничего не делать в конструкторе, а делать всё в обычной функции Init, что разбивает красивую идею конструкторов вдребезги).

C++ — ужасный, громоздкий, эклектичный, неудобный язык. Я не видел ни одной компании где StyleGuide не запрещал бы явным образом использовать те или иные возможности языка. Это говорит о многом — для других языков в StyleGuide описывается только количество отступов и принципы наименования функций… Единственная причина по которой его приходится использовать (на нём написана куча библиотек) к достоинствам собственно языка отношения не имеет.
У С++ есть четкая философия, почитайте хотя бы Страуструпа «Дизайн и эволюция С++» (знаю, не читали и читать не будете).

Java — ни в коем случае не пример для сравнения с C++, который по эффективности пока недостижим. Покажите мне язык, который по эффективности был сравним с С++ и имел бы существенно меньше недостатков? Что касается final в Java, я честно говоря не думаю, что он в реализации чем-то отличается от не-virtual в С++. Это такой же метод класса, а не объекта. Согласен, легче ошибиться не написав virtual, чем зря написав final, хотя это дело привычки, как == и =. Если же вы примете во внимание заточенность С++ под быстродействие, вы поймете, почему было сделано именно так.

Конструкторы в паре с деструкторами — это способ инициализации/разрушения объектов со своей философией. Тот способ, который предлагает Java тоже не лишен недостатков (взять хотя бы проблемы с освобождением ресурсов не-памяти).

А StyleGuide — вынужденная мера, поскольку настоящих спецов по C++ — единицы, остальные только думают, что его знают, поэтому городят на нем черт знает что и поругивают его от беспомощности.
У С++ есть четкая философия, почитайте хотя бы Страуструпа «Дизайн и эволюция С++» (знаю, не читали и читать не будете).
Почему же. Читал. Уже её достаточно для того, чтобы понять почему C++ оказался таким угробищем. Он был спроектирован как PL/1 — соберём кучу интересных идей из разных языков и засунем их в один. Так удобные, красивые языки не получаются. Так получаются Франкенштейны.

Java — ни в коем случае не пример для сравнения с C++, который по эффективности пока недостижим. Покажите мне язык, который по эффективности был сравним с С++ и имел бы существенно меньше недостатков?
А вы их сравнивали? Java последних версий проигрывает C++ на реальных задачах где-то 20-30% в скорости. Для многих задач это — некритично. 90% (хорошо если не 99%) написанного на C++ кода можно было бы спокойно переписать на других языках — и никто от этого особо не пострадал бы.

Что касается final в Java, я честно говоря не думаю, что он в реализации чем-то отличается от не-virtual в С++.
Ничем не отличается.

Согласен, легче ошибиться не написав virtual, чем зря написав final, хотя это дело привычки, как == и =.
Это не «дело привычки». Это вещь, которая уже унесла в общей сложности миллиарды долларов. Если вы «зря написали» final, то компилятор сообщит вам об ошибке, вы уберёте одну строку и всё. Если вы забудете слово virtual, то во многих случаях всё кончится многими часами отладки. Если бы невиртуальные функции нельзя было бы перекрывать, а от классов с невиртуальными деструкторами наследоваться, то «заточенность на быстродействие» ничуть бы не пострадала, но кучу времени и сил удалось бы съэкономить. Если бы пресловутую эквивалентность указателя и массива отменили бы для классов с виртуальными деструкторами — то ещё больше удалось бы съэкономить. Но это ж «специальные случаи»!!! Низя, низя, низя.

По-большому счёту уже C ужасен (упомянутое вами == vs =, автоматическое приведение от вещественных чисел к целым, etc), но он хотя бы невелик — все его «чудеса» можно по пальцам пересчитать. C++ усугубляет проблемы C++ и добавляет много новых…

Конструкторы в паре с деструкторами — это способ инициализации/разрушения объектов со своей философией. Тот способ, который предлагает Java тоже не лишен недостатков (взять хотя бы проблемы с освобождением ресурсов не-памяти).
Добавить RIAA в язык — не бог весть какая проблема. Можно сделать, скажем, как в python. А вот отсутствие нормальных механизмов отражений (reflection) приводит к тому, что все изобретают свои костыли (MOC, COM, CORBA, etc).

А StyleGuide — вынужденная мера, поскольку настоящих спецов по C++ — единицы, остальные только думают, что его знают, поэтому городят на нем черт знает что и поругивают его от беспомощности.
Нет — это отсуствие философии в языке. TIMTOWTDI (There is more than one way to do it) не работает для крупных комплексах, тут другой приницип нужен (There should be one — and preferably only one — obvious way to do it). StyleGuide эту проблему худо-бедно решает, но необходимость подобных действий язык он явно не красят.
> почитайте хотя бы Страуструпа «Дизайн и эволюция С++» (знаю, не читали и читать не будете).

Товарищ, прежде чем делать подобные заявления, я бы рекомендовал хотя бы пролистать комменты за авторством khim на его хабрацентре. khim — один из редких представителей вымирающего вида Programmator Sapiens. Будьте повежливее, пожалуйста!

> Покажите мне язык, который по эффективности был сравним с С++ и имел бы существенно меньше недостатков.

Понятие «эффективность» в данном контексте включает затраты на разработку алгоритма (написание, отладка)?

Если влючает, то C++ нервно курит в сторонке. Тот же Haskel, OCaml и даже Common Lisp (SBCL) будут гораздо эффективнее C++. Если мы рассматриваем исключительно эффективность кода — да, C++ рекордсмент.

Вопрос в том, кому сегодня нужна эффективность C++ от C++, когда топовые промышленные сервера содержат по 128 процессоров и по 2 терабайта RAM и есть ряд альтернативных языков, позволяющих сократить время разработки практически идентичного решения на тысячи процентов?
— Считаю, что я достаточно вежлив. Тем не менее, сомневаюсь, что он прочитал эту книжку, либо не умеет делать выводов. Все свойства языка там достаточно обоснованы. Например, он с самого начала разрабатывался, чтобы быть совместимым с миллионами строк уже написанного кода.

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

При том что есть множество языков, облегчающих разработку (здесь и мой любимец — Python), существуют (и всегда будут существовать) области и задачи, где важна и даже критична эффективность (по скорости, памяти и другим ресурсам). В конце концов, у вас же нет дома топовых промышленных серверов. Или есть? Делать апгрейд каждые полгода у меня, например, нет средств. И в телефон Core 2 Quad с даже пассивным охлаждением никак не засунешь, а как хотелось бы иметь там нормальную IP-телефонию и GPS навигатор.

— Про Haskel, Ocaml и лисп мне не говорите. На некоторых задачах они действительно удобнее, но до универсальности C++ им далеко. Посмотрите, хотя бы, как в ФП решается проблема ввода-вывода, это же ужас! Вообще я на ФП-языки смотрю с надеждой, что в них удастся, наконец достичь оптимального сочетания универсальности, эффективности и простоты, но пока это направление в зачаточном состоянии.

Общий вывод из дискуссии: разработать универсально гибкий, эффективный и одновременно простой язык — очень сложная задача, которая пока она не решена, и будет ли решена — неизвестно. А пока давайте будем уважать и те языки, которые решают проблему человеческого ресурса, и те, которые решают проблему ресурса машинного.
буду с нетерпением ждать ещё подобных статей, очень понравилось :)
Уж не придется ли немного отредактировать известное изречение таким вот образом: «Посадил дерево, вырастил сына, построил дом и написал статью про полиморфизм»
Не знаю, кто как(я лично увидел очень много восхищенных комментариев, что и отбило желание читать их дальше), не вижу смысла в этой статье, к тому же еще и в ненормальном программировании.

Статьи и целые книги, в которых ООП описано намного лучше и доступнее всех подобных статей здесь, были давным давно написаны, и сведены, как мне кажется, к оконечному своему виду, а написанное здесь — это всего лишь слегка модифицированный текст этих самых статей.
И эта, и все подобные статьи — это огромный баян.
Sign up to leave a comment.

Articles