Я давно вынашивал желание написать эту статью. И, наверное, мне бы стоило потратить некоторое время на то, чтобы написать её чуть более структурно и продуманно, но, пожалуй, я её в таком случае вообще не напишу, так что - статья будет ad hock, прям from the top of my mind.
Начнём с того, что в обсуждениях объектного программирования бытуют несколько популярных мнений
ОО появилось в ОО языках. Пришёл С++ и принёс нам объектность. А до него объектности не было. Тут не важно, что первым ОО языком был не С++ - важна сама мысль: объектную ориентированность принёс ОО язык, а до него она не существовала.
ОО есть этап на пути к более крутой парадигме, которая снесёт ОО, как ОО снёс структурное программирование. Мы находимся внутри эволюционного процесса. Бардачное спагетти программирование (Фортран), структурное программирование (Паскаль, отчасти Си), объектное программирование (С++, Ява) и его заменит, например, функциональное программирование.
Объектное программирование где-то, может быть, и уместно - например, в симуляторах, а где-то прям нафиг не сдалось. И вообще, когда меня учат объектному программированию на примере класса "животные", который ветвится в собачечек и кошечек, я понимаю, что объектная парадигма - фуфло, и только на собачках худо-бедно и работает. А в реальной жизни
собачекобъектов и классов нет.
Буду краток - все три пункта выше - фи-игня. А теперь приступим.
Давайте начнём с первого же пункта. ОО появилось в ОО языках.
Нет. ОО оформилось и осозналось в ОО языках, а появилось оно сильно раньше. Как будет видно ниже - оно появилось до программирования вообще, но не будем забегать вперёд.
Давайте возьмём операционную систему Юникс (ну - это как Линукс, только Линукс - это объект, а Юникс - это класс) и посмотрим на системный вызов write(). У него есть несколько интересных свойств, о которых вы, подозреваю, не задумывались.
Вы не видите реализацию этого вызова и не можете на неё влиять. Иногда это ещё называют инкапсуляцией.
Есть несколько разных реализаций этого вызова, которые реализованы различным образом и реализуют некоторые разновидности операции, обладающие характерной общей чертой - варианты для обычного файла, пайпа, сокета, блок-ориентированного и байт-ориентированного устройства. Это ещё зовут полиморфизмом.
Свойства вызова частично переиспользуются реализациями - например, запись в UDP сокет и запись в TCP сокет вызывают передачу данных в сеть. Запись в любой сокет вызывает передачу данных в сеть. Это ещё называют наследованием. (Вызов
writeтут не самый интересный пример -ioctlинтереснее.)
И - о чудо - мы вообще не рассматриваем никакой язык программирования, а уже имеем иерархию классов
абстрактный файл
- сокет
-- UDP
-- TCP
- дисковый файл
- устройство
-- блочное
-- байтовое
- пайп
и в ней - и инкапсуляцию, и наследование, и полиморфизм. И это я только один вызов привёл в пример, а там ещё ioctl есть, с множеством операций. Которые суть методы класса "файл". Кстати, запишите и абстрактные классы/интерфейсы.
Мало? Вот первая попавшаяся (честно!) мне на диске библиотека для языка Си.
TRex trex_compile(const TRexChar pattern, TRexChar error); void trex_free(TRex exp); TRexBool trex_match(TRex exp,const TRexChar text); TRexBool trex_search(TRex exp,const TRexChar text, const TRexChar** out_begin, const TRexChar** out_end); TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end); int trex_getsubexpcount(TRex* exp); TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
Что мы тут видим? А видим мы тут класс TRex, его конструктор, деструктор и его методы. Полиморфизма нет, но это просто библиотечка простенькая. В более сложных и полиморфизм есть, только в путь.
Так что: Объектная ориентированность в ОО языках не появилась, а была оформлена и поддержана языком. Появилась она в коде, написанном на языках предыдущего поколения.
Почему же она там появилась. Тому есть две причины. Одна более сермяжная - это следующий шаг по линии структуризации программ (инкапсуляция) и (в части про полиморфизм и наследование) инструмент переиспользования кода. Метод moveTo(x,y) нужен всем сущностям, которые реализуют интерфейс IMovable могут двигаться, и не надо его писать каждый раз заново.
Вторая причина чуть менее очевидна, но куда более значима.
Мы мыслим объектно!
Точнее - классово (не в смысле марксизма, нет:).
Каждый глагол и каждое прилагательное являются инструментами классификации. Красный - объект, имеющий цвет. Плыть - твари, способные не утонуть. И так далее.
Более того, в нашем языке есть абстрактные понятия, которые буквально определяют абстрактные классы. Инстанс такого класса без дальнейшей его детализации существовать не может, а вот если уточнить - то вполне.
Нельзя сделать объект типа "мебель", но можно конкретизировать абстрактный класс "мебель" до "стулья" и тут уже инстанс вполне жизнеспособен. А если он относится к подклассу "мягкие стулья", то даже желанен и мил.
Всё мышление человека - это выделение свойств (интерфейсы: пилить -> пригодный для распиливания) и классов (недвижимость -> дома -> школы). Мы просто никак иначе думать не умеем.
В этом плане замечательно одно странное русскому человеку свойство английского языка: артикли. Внимание, фокус: неопределённый артикль вводит класс, а определённый - описывает объект.
Это настолько чётко соответствует правилам ОО, что даже меня немного ошарашивает: a table - стол вообще, класс "столы", а the table - инстанс, совершенно определённый и данный нам в ощущениях.
Теперь, ради справедливости, ложка дёгтя. Кратко: никакие классификации реального мира не полны, не однозначны и не всеобъемлющи. Все реальные объекты принадлежат к тысяче разных классов одновременно и какой-то из них может быть предпочтительным только ситуативно. Нам важно, чтобы стол был красивый когда мы его покупаем, и чтобы он был деревянный, когда наводнение и мы бы хотели на нём уплыть.
Классификации ОО языков жестки, однозначны и являют собой изрядное прокрустово ложе.
С другой стороны - всё программирование вообще есть упрощение. Кастрированная модель предметной области. Потому что полная модель предметной области это только сама предметная область. Модель всегда что-то опускает и упрощает - это свойство программирования вообще, а не ОО в частности.
Тем не менее, подводя итог (в нашем веке в конце статьи читателю обязательно надо объяснить, что именно он в статье узнал - сам-то он не в курсе):
Объектная ориентированность - ключевое, основополагающее свойство мышления человека, и как таковое не есть что-то искусственно привнесённое в языки программирования. Оно с нами издревле и навсегда. Во всяком случае, пока существует вид homo sapiens sapiens.
(И да - ИИ, судя по всему, "мыслит" не классами.)
Комментарии (28)

yahooyaks
19.04.2026 10:17Парадигмы в программировании понадобились, чтобы упоавлять сложностью, чтобы упростить восприятие кода в частности. Разработчику надо быть хоть немного архитектором, чтобы осознанно выбирать тот или иной подход в реализации решения. Там где объекты уместны, там применение ООП снижает нагрузку и повышает эффективность ппрограммиста. А в статье очередное нытьё!

Emelian
19.04.2026 10:17Разработчику надо быть хоть немного архитектором, чтобы осознанно выбирать тот или иной подход в реализации решения. Там где объекты уместны, там применение ООП снижает нагрузку и повышает эффективность программиста.
Точно!
Поэтому считаю, что: «объекты и классы – дар Божий». Они очень хорошо позволяют структурировать код, выносить модули в автономные классы. А там, где слегка накосячил в первой версии программы, как у меня:

Структура первой версии обучающей программы «L'école» то, используя более трезвый подход, можно, на тех же принципах, ваять уже усовершенствованную версию своей программы, что я, сейчас, и делаю.

nemavasi
19.04.2026 10:17В ООП языках объекты часто стали превращаться в минибиблиотеки функций. И в проектах чаще вижу в коде не объекты, а дтошки с сервисами. Отделены код и данные. Наличие ООП объектов в коде не приводит автоматически к правильной модульности и минимизацией зависимостей. Это лишь один из способов структурирования кода. И не самый простой. Один разработчик считает, что документ должен иметь метод "Печатайся" с передачей ему объекта, куда печататься. Другой считает, что должен быть объект Принтер, имеющий метод "печатать", принимающий документ на вход. Это серьезная когнитивная нагрузка, каждый раз выбирать. ООП легче все же при симуляции конкретного процесса, лучше физического.. Там легче выбирать, так как ты его отчасти копипастишь с природы. Но когда ты имеешь дело с данными, которые живут в разных процессах, и могут пережить старые процессы, перекатившись в новые, то, гораздо проще иметь дело со более универсальными структурами данных и функциями, умеющими работать с ними. Но это мое личное мнение, не больше. У меня есть статья, тоже написанная на эмоциях на эту тему )

dzavalishin Автор
19.04.2026 10:17Пример, конечно, неудачный. Метод можно поместить куда угодно если он не трогает потроха класса и у нас вообще нет наследования. Что явно вырожденный случай. Если наследование есть, и оно предполагает разную реализацию печати для разных принтеров или для разных документов - то расположение метода очевидно.
Ну а разные процессы - это объекты обёрнутые в JSON-ы, вроде бы так.

GBR-613
19.04.2026 10:17Во-первых. "Есть несколько разных реализаций этого вызова.... Это ещё зовут полиморфизмом"
Вроде бы это зовут абстракцией?
Во-вторых, насколько я знаю, "a table" значит "какой-то инстанс", или "некий инстанс". Класс это просто table, без артикля.
Безусловно , ООП это не свойство языка, свидетельством чего являются первые версии Windows: ООП в чистом виде, и при этом на ANSI C.
На моих первых работах я в основном писал свой код, с нуля или почти с нуля. Я очень любил ООП и С++: все можно было сделать проще и понятнее. Потом так получилось, что я в основном читал чужой код и чинил там баги или делал небольшие добавки. И тут я ООП разлюбил. Когда длинная цепочка наследования, происходит что-то непонятное, и поди знай, откуда это выплывает, с какого уровня... Когда в суперклассе есть то, что мне нужно, а я не могу это использовать, потому что метод не protected, a private...
Как то раз в коде было написано что-то типа "х := у + 1". Результат: у был 0, х стал -1. Сначала я решил, что у меня сломался процессор :-) Оказалось, нет: кто-то перегрузил операторы "-" и "+", и в определённых условиях они срабатывали наоборот.

dzavalishin Автор
19.04.2026 10:17Это проблема реализации ООП в ++. Собственно, поэтому в Java и большинстве последующих языков перегрузку операторов отменили.

dzavalishin Автор
19.04.2026 10:17Насчёт "какой-то инстанс" Вы правы, но это и есть введение класса, по сути. Мы не знаем конкретных свойств этого инстанса (значений полей и даже конкретного класса). Ну, то есть, даже не знаем, кухонный это тейбл, или верстак.

kreofil
19.04.2026 10:17Вообще-то в Линуксе издревле основным понятием был процесс, а не объект. А по поводу парадигм и программирования существуют и другие мнения. Например здесь: http://softcraft.ru/

dzavalishin Автор
19.04.2026 10:17Слова "издревле" и "линукс" несовместимы. Издревле существует Bell Labs Unix. Основных понятий в нём два. Процесс и файл.

XViivi
19.04.2026 10:17не столько некое замечание, сколько некая мысль, о которой я подумал во время чтения статьи
отдельно интересно мыслить из категории ооп без наследования классов, как в расте или хаскеле; а то и вовсе некую вырожденную категорию где есть строго только синглтоны ну или каждую структуру можно "инстанцировать" лишь единожды
по сути, исходя из такой, каждый объект характеризуется лишь своими свойствами. есть интерфейс/трейт "стул", характеризующийся тем что на нём можно сидеть и ещё какими-то свойствами.
есть трейт "деревянный стул" — по сути, объединение трейтов "стул" и "деревянный объект" + какие-то свои отдельные свойства.
и есть некий объект "The деревянный стул сделанный на фабрике в Швейцарии в 1998 году пекарем Николой Теслой во время запекания сапогов, украденных у соседки Зины, пока она …, масса которого составляет 10кг, состоит в данный момент из x.xE+27 атомов, …" — каждый такой объект уникален и каждый удовлетворяет каждый лишь строго своему набору трейтов.
каждый трейт это просто объединение других трейтов (ну или какой-то атомарный трейт по типу как условный трейт "объект со скоростью 1 м/с"), а каждый объект это некая уникальная сущность
хотя возможно, для такой модели ближе не oop, а ecs. тогда, сервисы — это те самые трейты, а компоненты… не уверен. возможно, сервисы — тоже компоненты, просто динамические? а объекты это как раз entity.
впрочем, из конкретно примера со стулом, можно ещё и привести непонятки, навроде парадокса корабля тесея… ну или что если взять половину атомов стула и использовать для создания одного стула, а половину для создания другого… будет ли хоть один из новых стульев считаться оригинальным объектом? как будто тогда уж стоит говорить и о уникальности во времени, ну или иммутабельности, что, как мне кажется, точно лишь запутывает…

dzavalishin Автор
19.04.2026 10:17Можно практически всё, но зачем? Наследование - способ структуризации и представлений о предметной области, и связанных с ним применимых к ней алгоритмов. Представить себе мир, где объекты компонуются из необъятного моря трейтов легко, но они же не будут ортогональны. Придётся сделать трейт "украдены у Зины" и трейт "украдены у Люси", вместо трейта "украдены у кого-то" и его конкретизации.
Я делал в своё время в Яндексе иерархическую ОО БД, в которой иерархия была именно и только иерархией наследования. То есть в ветке дерева можно было либо заявить новое свойство, либо зафиксировать значение заявленного выше свойства. Оказалось, что иерархия наследования самодостаточна и позволяет заменить собой иерархию хранения.
Это, конечно, немного взрывало мозг, но логика была безупречна. (Она делалась для Маркета) - на каждом уровне иерархии появлялись поля, актуальные для данного обобщения. И, кстати, множественное наследование работало и позволяло из "принтеры" и "сканеры" родить "МФУ".

amcured
19.04.2026 10:17Придётся сделать трейт "украдены у Зины" и трейт "украдены у Люси", вместо трейта "украдены у кого-то" и его конкретизации.
Это почему еще? Завтипы прекрасно эту конкретную проблему решают.
Вообще говоря, полиморфизм и инкапсуляция — как-то внезапно оказались узурпированы ОО-евангелистами, хотя и появились раньше, и существовали во всех мало-мальски используемых языках. Файл открыл в блокирующем режиме — и вот она инкапсуляция.
void**— и вот он полиморфизм (или с теми же файлами — soft link на файл).Наследование же, которое остаётся действительной, настоящей прерогативой ОО — навряд ли сильно облегчает жизнь и уж точно не помогает описывать мир. (Пример с артиклями очень красивый и яркий, но это пока не наследование, это пара интерфейс→реализация, от которых и в ФП не продохнуть, и на которых везде реализован тот же полиморфизм.)
Не, ну серьезно, мебель→стол — это прям полезно: «Что нужно? — Стол. Куда ехать? — В мебельный!». А что насчет наследования от стола? Почему его нельзя реализовать свойствами вместо методов? Связанными объектами? Внедренными зависимостями, на крайняк?
Так что текст выше — очень изящный и внятный, я получил настоящее удовольствие от казуистических трюков. Но тезис «ОО очень хорошо ложится на окружающий мир» — не подтвержден буквально ничем. Меж тем, Аланкаевская модель с сообщениями — вот она действительно блистательно описывает мир, ибо объекты гораздо менее важны, в сравнении с обменом информацией. ОБ этом очень много говорил Армстронг, и его реализация «окружающего мира» — гораздо убедительнее Гослинговой.

chaetal
19.04.2026 10:17Но тезис «ОО очень хорошо ложится на окружающий мир» — не подтвержден буквально ничем. Меж тем, Аланкаевская модель с сообщениями — вот она действительно блистательно описывает мир, ибо объекты гораздо менее важны, в сравнении с обменом информацией. ОБ этом очень много говорил Армстронг, и его реализация «окружающего мира» — гораздо убедительнее Гослинговой.
За это вам наша искренняя сердечная благодарность!
Настоящий ("аланкеевский") ООП не просто хорошо, а изумительно хорошо описывает буквально всё. При этом всего-то и нужны объекты и сообщения.
Универсальность * Простота = МОЩЬ
И не нужны никакие краденые и переиначенные инкапсуляции, полиморфизмы, которые только усложняют и путают. И даже наследования с абстракциями совсем не обязательны. Это всё прекрасно выводится из объектов и сообщений.
Современный "ООП" на столько исковеркал саму идею, что все эти споры остроконечников с тупоконечниками уже просто смешны — про то, что яйцо вариили, чтобы его просто съесть, никто даже не вспоминает. …Нет, уже даже и не знает, похоже.

amcured
19.04.2026 10:17не нужны никакие […] инкапсуляции, полиморфизмы, которые только усложняют и путают
Вместе с водой ребенка-то не надо выплескивать :)
Без полиморфизма (который, как я выше отметил, не является прерогативой OOП ни в какой мере) — мы бы замучались реализовывать банальные
Eq,OrdиShow. Без инкапсуляции (которая с успехом заменяется полной иммутабельностью, и является, по сути, костылем вокруг изменяемых откуда попало данных) — фактически невозможно рассуждать о коде. Это все крайне полезные штуки, на одних сообщениях далеко не уедешь, кроме аксиоматики — нужен и минимальный понятийный аппарат.Кто знает, если бы инфоцыганщина Мартина не выстрелила в неокрепшие умы джейсоноукладчиков, — и ООП был бы не так уж изуродован популистским бредом. Так-то и на джаве можно написать вполне себе рабочие штуки (правда, все, кто делал что-то стоящее, а не бухучет, — рано, или поздно, переходили на Скалу: Hadoop, Spark, Akka).

chaetal
19.04.2026 10:17"Минимальный понятийный аппарат" — это вы про полиморфизм и инкапсуляцию? А я почему-то думал, понятийный аппарат должен быть хотя бы более однозначным и более простым, чем то, для описания чего он используется.
Как странно получается… Про сообщения и объекты вы в курсе. Про то, что на их основе сделаны Smalltalk/Self, Erlang (с оговорками, но всё же) и акторы — видимо, тоже? Про то, что "инкапсуляция" с "полиморфизмом" в отношении ООП появились позже, наверное, как минимум, догадываетесь. Но для описания очевидных следствий из "аксиоматики" всё равно почему-то считаете необходимым введение "понятийного аппарата" на основе переусложнённого и искажённого "полиморфизма" и шизофреничной "инкапсуляции". А чем они лучше простейших и интуитивно понятных, наверное, любому школьнику "поведения" и "состояния"? В последних по вашему мнению чего-то не хватает? Чего?

amcured
19.04.2026 10:17А я почему-то думал, понятийный аппарат должен быть хотя бы более однозначным и более простым, чем то, для описания чего он используется.
Дайте угадаю, вы никогда не сталкивались с понятийными аппаратами современной математики (всякие там аффинные геометрии, теории групп, и т. п.).
А чем они лучше простейших и интуитивно понятных, наверное, любому школьнику «поведения» и «состояния»?
Тем, что помимо аксиоматики любой теории нужен набор теорем, иначе в аксиоматике Пеано замучаешься каждый раз с нуля доказывать коммутативность.
В последних [«поведение» и «состояние» — @amcured] по вашему мнению чего-то не хватает? Чего?
Прилагательных и наречий :) Поведение — это глагол. Состояние — существительное. И пока мы с вами — не Бродский, — нам не обойтись одними глаголами. «Красный» — это не поведение и не состояние.

XViivi
19.04.2026 10:17В другом комментарии уже и так всё объяснили но всё же, объясню и от себя
Придётся сделать трейт "украдены у Зины" и трейт "украдены у Люси", вместо трейта "украдены у кого-то" и его конкретизации.
trait Stolen { const from : &str; } // или так, игнорируя, что константным параметром могут быть только инты, предполагая что объект может быть украден по нескольку раз trait Stolen<const from : &str> {}в идеале, если будут зависимые типы, позволяющие сделать константы не такими уж и константами — что хорошо ляжет на изменчивость при использовании разных объектов. Потом может появиться что-то такое :)
И, кстати, множественное наследование работало и позволяло из "принтеры" и "сканеры" родить "МФУ".
Вообще я немного думал о наследовании и мне кажется, что возможность наследовать только методы (интерфейсы) полностью покрывает возможности наследования с полями. А если базовый класс позволяет задавать поведение (как делают Unity или Javafx), его тогда странно, что с самого начала не заменят на пару из композиции и наследования от некого интерфейса/трейта
// вместо abstract class Parent { int a; } class Child : Parent { ... } //делать как-то так interface Parent { protected int a {get; set;} } sealed class Child : Parent { // бонусом ещё и видно на месте, // так что не придётся лезть в базовые классы за определением protected int a {get; set;} ... }но наверное, в бд это может быть какой-то отдельный случай, где было удобно сделать именно так, я не сталкивался с таким, наверное.
да возможно моя мысль окажется непрактичной, я не могу сказать.

chaetal
19.04.2026 10:17Если бы к "фиговым" трём пунктам добавить ещё и самый неправильный про то, что "ООП — это инкапсуляция, наследование, полиморфизм", могло бы получиться что-то дельное. А так, к сожалению, получился очередной наброс в спор глухих со слепыми о балете.

dzavalishin Автор
19.04.2026 10:17Предполагается, что уж определения с первой страницы букваря читатель знает. :) Читателю, который этого не знает и сам вопрос, наверное, не очень интересен...

chaetal
19.04.2026 10:17Кто-то кого-то не понимает — либо я вас, либо вы меня. Давайте выясним:
Буду краток - все три пункта выше - фи-игня.
Вот и четвёртый (я бы его на первое место поставил) пункт про инкапсуляцию, наследование, полиморфизм — не меньшая фи-игня. Да?

dzavalishin Автор
19.04.2026 10:17Э. Стоп. Это общепринятое определение. Его можно тоже снести, но тогда надо свое предложить. Я такой задачи перед собой не ставил.

chaetal
19.04.2026 10:17Всё уже давно предложено. С самого начала. И благополучно забыто.
В целом, позиция ясна.

Yami-no-Ryuu
19.04.2026 10:17Что-то в этом есть. Чем не классы?
module type ACC = sig type t val add: string -> t -> t end module NoAcc = struct type t = unit let add _ () = () end module type DEPS_ENV = sig module Acc: ACC val deps: unit -> JSON.value val name: JSON.value val yes: bool end module Deps = struct module A = Set.Make(String) (* implements ACC *) (* A bit of an artificial example *) module Inherit = struct include A let add k x = prerr_endline "Inherited" ; A.add k x ;; end
gev
И ты, Брут?
dzavalishin Автор
Кто-то же должен подвести черту :)
gev
Было бы под чем ;)