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

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

Одна из существенных проблем ООП, кмк, — реализация операций. То есть на классическую парадигму оно очень плохо ложится. Сложение чисел или конкатенация — тому яркий пример: objA.plus(objB) или objB.plus(objA)? (я в курсе про хелперы, но их существование не укладывается в классическую парадигму).

И тут же всплывает еще один очевидный вопрос — операция должна вернуть новый объект или модифицировать существующий? В этом месте у ФП есть положительная сторона — идеология топит за иммутабельность всегда.

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

Наследование — тоже хорошо там, где оно уместно. Проблема только в том, что оно резко сжимает пространство маневра при разработке сложных систем, композиция гибче. В реальной жизни мы используем одни и те же предметы как принадлежащие разным иерархиям наследования, просто не задумываемся об этом. В ООП же объект может принадлежать только одной иерархии (про cpp в курсе).
Реализация операций не зависит от ООП и в некоторых языках решается перегрузкой операторов.
Проблема тут не в реализации, а в том как обеспечить полиморфизм.
Наверное, все-таки разговор про иммутабельность?
В первом абзаце — нет, не про иммутабельность.
Тсс, а то Егор Бугаенко с адептами набегут!
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
тому яркий пример: objA.plus(objB) или objB.plus(objA)? (я в курсе про хелперы, но их существование не укладывается в классическую парадигму).

Не совсем понимаю где проблема.


std::string a("123");
std::string b("444");
const std::string a{"123123"};

std::string c{a+b};
std::string d{b+a};

a+=aa;

aa+="xxx"; // compiler throws an error here

По моему всё семантически чётко и ясно. Или всё-таки где-то проблемы?

>>const std::string a{«123123»};

должно быть
const std::string aa{«123123»};
Что делать, если мне часто нужно переворачивать строку?
Писать метод в функциональном стиле
std::string reverse(const std::string& src);

или в процедурном
void reverse(std::string& str);

Но в ООП это никак не заходит.

Угу опять вопрос мутабельности. Это холивар. А утилитарно всё зависит от цели:


  1. std::reverse() — swap
  2. std::string::rbegin() — реверсный итератор
  3. std::string::crbegin — константный реверсный итератор

Вы действительно считаете, что эта проблема "буриданова осла", кого-то остановит от желания программировать на C++ или в ООП парадигме в целом? Дать право программисту выбрать длительность жизненного цикла объекта и доступа к нему, или отобрать? Совсем! Чтоб неповадно было.


Вопрос выбора инструмента, дело каждого конкретного программиста. Всё это вкусовщина. Небо наземлю не упадёт если объекты будут mutable.


Вот вам гениальный пример для батхёрта (пусть стошнит каждого функциональщика):


class A {
  mutable std::mutex __mutex;
  std::string              __str;
public:
  void  set(const std::string& str)
  {
     std::lock_guard<std::mutex> sync(__mutex);
    __str=str;
  }
  const std::string get() const
  {
     std::lock_guard<std::mutex> sync(__mutex);
     return __str;
  }
};

Ух пронесло, — небо не упало!


Вы не поверите, люди до сих пор пишут на чистом C! Тут в ветке даже Дельфисты отметились.


Вам шашечки или ехать?

кого-то остановит от желания программировать на C++ или в ООП парадигме в целом?
На C++ можно программировать и в функциональном стиле.

Вам шашечки или ехать?
Я отвечал на
>> objA.plus(objB) не укладывается в классическую парадигму
> Не совсем понимаю где проблема

Что есть неудобства в ООП, когда объекты предоставлены из внешней (стандартной) библиотеки, и тут приходится съезжать на ПП или ФП.

Сам-то я не против ООП, но не отрицаю его проблемы.
Это типа extension methods, подсмотренные в c#?
Там же чёрным по белому написано: любую функцию можно вызвать как метод её первого аргумента.
Если это способ реализовать недостающие функции объектов, к коду которых нет доступа, то это просто костыль, синтаксический сахар ))

В сравнении с полноценным наследованием, тут недоступны protected-поля и методы, оставленные автором объекта для расширения его функциональности. Нельзя добавить новое поле, и т.п. ограничения.
То, о чём вы говорите — это уже monkey patching и от него больше вреда, чем пользы.

Любую мономорфную функцию в языке без подтипирования.
Все-таки при формулировке теоремы не стоит забывать про условия.

семантически чётко и ясно. Или всё-таки где-то проблемы?
Это потому что конкатенация предусмотрена создателями библиотеки std. Но описание перегрузки оператора умножения над двумя std::vector«double», чтобы считалось скалярное произведение, выходит за рамки ООП.

Вы наверное имели в виду STL. И что вам мешает следовать хорошему тону STL, и писать такие-же библиотеки. В STL кстати есть functional в котором в том числе ести и функциональный класс std::multiplies.


Самое главное, вас ведь никто не ограничивает писать то как вы хотите и как считаете нужным, главное что-бы вам удобно было. А зависать над вопросом "в какой парадигме мне это реализовать", — не продуктивно. Выбирайте хоть ФП, хоть ООП, хоть haskel хоть Java. И реализуйте это то как считаете верным. Какой смысл в войне между отвёрткой и гаечным ключём? Никакой! И тот и другой инструмент может понадобится.

НЛО прилетело и опубликовало эту надпись здесь
несколько операторов в одной строке [не нашел нужного научного термина, но думаю суть понятна]

Кортеж?
НЛО прилетело и опубликовало эту надпись здесь
Разумеется модифицировать существующий. Мембер принадлежит обьекту, значит и работает он с обьектом, какой то вопрос глупый, с чего бы вообще операции создавать новый обьект новый обьект создается конструктором, или фабрикой или мембером который явно определяет создание новое обьекта если мы говорим о колекции но никак операция над обьектом не должна создавать явно новый обьект оке мы можем создать мутабл переменную но в общем случае конечный программист использующий интерфейс обьекта об этом никогда не думает, ну и с каких пор обьектно ориентированное программирование использует plus? Это что за сумрачный гений это придумал, есть функции которые нативно выглядят как операторы языка, и вообще что плохого в перегрузке, то что она неявная оргамунт уровня add неявно — разумеется если вы используете класс не интегрального(фундаментального типа) с перегрузкой оператора вы ожидаете особого поведения сематически эквивалетного сложения к примеру для матриц или строк, снова какие то обсолютно выдуманные аргументы в пользу фп и иммутабельности, ооп никогда не топил за создание новго объекта при действиях типа сложения и операция на сематическую адекватность, крч, снова проблемы возникающие в результате отсутсвия вопроса самому себе — а что должна делать функция и кривых интерфейсах.

Вопрос не глупый. Например 1.Add(2) явно должен создавать новый объект 3, а не менять первый константный аргумент.

Это потому, что у вас слева rvalue. А вот так совсем не явно:


auto foo = 1
foo.Add(2) // foo += 2
у вас равно — это присвоение или алиас? Потому что если мы смували `1` в `foo`, а потом изменением его, то изменятся все единицы во всей программе. Это явно не то, чего мы хотим.

Присвоение, разумеется. Изменится лишь локальная переменная.

В общем, вы не правы в общем случае. Метод может модифицировать объект, но не обязан. Куча апи на этом построена, например вот.
А вот подменять тезисы, пожалуйста, не надо.
Мембер принадлежит обьекту, значит и работает он с обьектом, какой то вопрос глупый, с чего бы вообще операции создавать новый обьект

Вопрос не глупый. Например 1.Add(2) явно должен создавать новый объект 3, а не менять первый константный аргумент.

Эмм, где подмена?
Логическое отрицание и антоним — разные вещи.

Реализуете интерфейс Add и все. Сигнатура этого метода явно должна сказать, что происходит. Например вот.


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

Инкапсуляция — это вообще свойство любой программы в любой парадигме. Вот написали вы let loadFromDatabase = loadFrom database — инкапсулировали загрузку из бд! Только вот ООП тут ни на гран.


Наследование — тоже хорошо там, где оно уместно. Проблема только в том, что оно резко сжимает пространство маневра при разработке сложных систем, композиция гибче. В реальной жизни мы используем одни и те же предметы как принадлежащие разным иерархиям наследования, просто не задумываемся об этом. В ООП же объект может принадлежать только одной иерархии (про cpp в курсе).

Композиция и наследование ортогональны. Спросити у растовчан, насколько им приятно писать UI без наследования, услышите массу интересного.

Если, например, говорят про процедурное программирование, то говорят про него абсолютно спокойно

Чистое процедурное программирование — это треш, угар и содомия с логарифмическим графом зависимостей скорости разработки от количества кода. Говоря проще: Больше кода и больше проблем, где-то в некой точке X переходящей в уничтожение как личности каждого, кто попытается добавить туда ещё какую-нибудь процедурку/функционал.

Это самый очевидный подход для любого новичка и совершенно мёртвый в современном мире. Хотя есть и исключения, вроде такой известной штуки как Wordpress, которую пока не материл только ленивый.

По-моему о процедурном подходе как раз и не говорят ничего плохого, т.к. это удел либо мелких одноразовых тулзовин, либо работы новчиков, которые (новички т.е.) потом перерастут этот стиль и начнут писать более адекватный код без засирания состоянием всей программы.

P.S. А на ООП сваливаются все камни по той причине, что это некое «над» решение, которое успешно совмещает (в реальной жизни имеется ввиду, а не сферическом ваккууме) под своим крылом и функциональщину, и процедурщину, и декларативное, аспектное, контрактное, прототипное и прочие программирования. Это стержень архитектуры, а остальное — это лишь решение частных мелких проблем/алогритмов (ФП — это вообще ярковыраженный представитель таких решений, где для решения частной задачи надо понаклепать с десяток функций, которые потом композицией выстраиваются в красивую программку).
НЛО прилетело и опубликовало эту надпись здесь
В процедурном подходе никто не мешает вам иметь локальные состояния. Более того, некоторые рассматривают ООП в целом лишь как идею организации процедурного кода, а ООП возможности языков как синтаксический сахар, без которого можно обойтись и написать что-то вроде GTK в парадигме ООП но без сахара.
Напомните, на чём там написаны ядра linux, windows или freebsd?
Когда писались эти ядра, хорошо проработанной и реализованной концепции ООП еще не было. В результате получили такое чудо как Win API. Которое только Delphi смогло к более-менее удобоваримому и однообразному виду привести. Яркий пример, что может получится в результате использования ФП, к слову.
Странно приводить VCL как пример хорошей обёртки над WinAPI. VCL в Delphi ужасен и тянет груз совместимости с Win16. Было дело — использовал такую вещь как Watcom Optima — это тот же Delphi, только C++ и не отягощённый грузом совместимости — всё было гораздо проще, компактнее и по возможностям базовой библиотеки превосходило Delphi. (В своё время много использовал и Delphi и напрямую WinAPI и свои не-Delphi-йские компонентные системы над Win32 API делал и делал backports на Win16.)
Ну, первая версия ядра linux — 91 год, а C++ появился в 83. То есть времени для появления более-менее надёжных практик достаточно. А вот намного более поздняя HaikuOS уже в основном написана на C++ (кстати, BeAPI офигенная штука, намного удобнее WinAPI), хотя отдельные части всё ещё C.

Ещё пример: микроконтроллеры. Довольно мало любителей писать под них на плюсах. Ну, потому что ограничение ресурсов слишком жёсткое. Это уже не говоря про FPGA, где свой маленький мир.

И не забудьте про мои любимые шелл-скрипты, которые чуть менее, чем полностью состоят из процедурного подхода.

Я не говорю, что C++ и ООП плохо, просто есть места, где их использовать не стоит.
НЛО прилетело и опубликовало эту надпись здесь
Не знаю кода этих ОС, но знаю код некоторых других проектов на С, и он похож на ООП подход. Например, если рассмотреть модульность ffmpeg (как в libavcodec). В ОС, мне кажется, не обойтись без объектного мышления.
Чистое процедурное программирование — это треш, угар и содомия

Это самый очевидный подход для любого новичка и совершенно мёртвый в современном мире.

По-моему о процедурном подходе как раз и не говорят ничего плохого, т.к. это удел либо мелких одноразовых тулзовин

Расскажите это: Линусу Торвальдсу или создателям почти любой ОС, создателям apache, nginx, tarantul, и т.д.


Выбор инструмента, это исключительная прерогатива создателя ПО.

Расскажите это: Линусу Торвальдсу или создателям почти любой ОС, создателям apache, nginx, tarantul, и т.д.
Вот только они не написаны в чистом процедурном стиле. То что в C нет классов не значит что в нем нельзя использовать ООП.
НЛО прилетело и опубликовало эту надпись здесь
Да, большинство современных проектов на C реализованны именно в рамках объектно-ориентированной парадигмы. В ручную:)
Одна из существенных проблем ООП, кмк, — реализация операций. То есть на классическую парадигму оно очень плохо ложится. Сложение чисел или конкатенация — тому яркий пример: objA.plus(objB) или objB.plus(objA)

Если перекладывать эту операцию на реальный мир, то всё зависит от того, что мы делаем. При наличии у нас двух коробок с апельсинами, мы можем переложить их из одной в другую, тем самым изменив состояния одной из коробок. При этом мы можем все апельсины переложить в третью коробку, тем самым создав новый объект (заодно выкидываем старые коробки). То есть всё зависит от конкретных требований.
Я вообще слабо представляю системы порядка единиц/десятков миллиона строк кода написанных в фп парадигме. Как написавший 2/3 от нескольких миллиннострочных проектов.
Также не совсем очевидны выгоды от иммутабельности. Возможно в каких-то случаях она полезна, сложно сказать. Как по мне — то проще найти кто меняет данные и разобраться с проблемами, чем запрещать мутабельность вообще. Может, конечно, языки такие, что это сделать сложно или невозможно. В Delphi это делается элементарно.
Тут ключевой вопрос не «кто может менять объект», а «когда его могут менять».

В частности, иммутабельный объект всегда потокобезопасен и реентерабелен без дополнительных усилий. В отличии от мутабельных объектов.
Тут есть какой нюанс. Иммутабельность довольно просто 'эмулируется' read only свойствами/инкапсуляцией. То есть — нужно — сделали иммутабельным. Не нужно — сделали обычным. А вот в тех языках, где всё иммутабельное выбора, увы, нет.
А вот в тех языках, где всё иммутабельное выбора, увы, нет.

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

Иммутабельность полезна в случаях, когда создаётся кусок данных (объект), который используется в нескольких местах и/или в разных потоках. Тогда вероятность того, что такой объект ВНЕЗАПНО поменяется, равна нулю. К слову, иммутабельность легко реализуется на любых ООП-языках (может, и не только ООП) — достаточно не писать сеттеров и не менять поля объекта кроме как в конструкторе. А вот наоборот — "чуть" сложнее.

Стандартный вопрос на собеседовании: «как реализовать неизменяемый объект?» и на ответ, данный вами
достаточно не писать сеттеров и не менять поля объекта кроме как в конструкторе
стандартный дополнительный вопрос: «а если я в конструктор передам изменяемый nestedObject? И потом изменю его состояние?». В результате беседы приходим к выводу, что вся иерархия объектов должна быть неизменяемой. И что вручную это сделать накладно, если в языке какой-нибудь Integer или String по своей природе mutable.
P.S. В основном я с вами согласен — неизменяемость полезна при многопоточности и может здорово упростить жизнь. Я лишь хочу подчеркнуть, что гарантии неизменяемости «из коробки» полезны. Особенно если есть четкая грань, что mutable а что нет. Например scala.mutable.Array vs scala.immutable.List — сразу понятно что к чему даже новичку в языке.
> «а если я в конструктор передам изменяемый nestedObject? И потом изменю его состояние?».

То ничего не изменится — наш объект останется иммутабельным (если забыть про рефлексию и подобные трюки). :)
а если я в конструктор передам изменяемый nestedObject? И потом изменю его состояние?

Вы просто не сможете это сделать:


Error: cannot implicitly convert expression bar of type Bar to immutable(Bar)
Я придрался к фразе
иммутабельность легко реализуется на любых ООП-языках (может, и не только ООП) — достаточно не писать сеттеров и не менять поля объекта кроме как в конструкторе
В вашем примере неизменяемость реализована за счет ключевого слова immutable — такое есть не во всех языках.
Вот код на java, показывающий идею
class Compensation {
	int salary;
	int yearPremium;
	public int getSalary() {return salary;}
	public int getYearPremium() {return yearPremium;}
}

class Employee {
	private Compensation compensation;

	public Employee(Compensation compensation) {
		this.compensation = compensation;
	}

	public int calculateYearCost() {
		return compensation.getYearPremium() + 12 * compensation.getSalary();
	}
}

@Test
public void testImmutable() {
	Compensation offer = new Compensation();
	offer.yearPremium = 1000;
	offer.salary = 1000;
	Employee newcomer = new Employee(offer);
	offer.yearPremium = 1_000_000;
	assertEquals(newcomer.calculateYearCost(), 13_000);
}

Злобный хакер баг-хантер получит премию, а на собеседовании стоит внимательно слушать вопросы ;-)
Главный минус и главный же плюс использования иммутабельности в хорошо знакомых мне ООП языках — сравнение по ссылке говорит изменялась ли сущность, но не говорит, что, возможно, состояние вернулось в начальное после нескольких изменений и никак реагировать на них не нужно.
Для этого, если мне не изменяет память, существует шаблон Publish-Subscribe, чтобы ООП-парадигма в отдельно взятой реализации не превратилась в Eiffel.
НЛО прилетело и опубликовало эту надпись здесь
Не понял о каком времени речь.
НЛО прилетело и опубликовало эту надпись здесь
В каком времени? В реальном или моделируемом?
НЛО прилетело и опубликовало эту надпись здесь
Чем дальше, тем меньше понимаю о чём речь.
НЛО прилетело и опубликовало эту надпись здесь
Как-то вы ВНЕЗАПНО перескочили к безопасности сервера и небезопасности клиента, хотя ничего, НИЧЕГО! Не предвещало этого. Я даже капсить начал, чтобы обозначить, насколько внезапный и неуместный, слово детский понос в метро, был ваш комментарий.
Вы упустили один важный момент в сравнении наследования и композиции — наследование оптимизирует память (меньше объектов) и скорость (меньше cpu циклов на создание а потом и уборку объектов). Когда имеется например 10 последовательных переопределений поведения или добавлений нового функционала (для ui-разработки компонентов это распространенная ситуация) то для какого-нибудь списка из тысячи компонентов при использовании композиции будет создано 10 тысяч объектов а при использовании наследования в десять раз меньше — 1 тысяча объектов. А если будет цепочка из 20-ти переопределений или расширений то используя композицию будет создано уже в 20 раз больше объектов и т.д — то есть сколько бы цепочек не городили бы с наследованием количество объектов нисколько не увеличивается в отличие от композиции
Это касается только языков, которые не умеют выделять память под композицию объектов как одно целое. Например, в C++ не никакой разницы, делать
class InputDevice : public Device { ... }
или
class InputDevice {
   public: Device device;
}

Вы имеете ввиду аллокацию на стеке? А нет ли в с++ оверхеда по памяти на сам объект класса? То есть если внутри класса Device будет еще одно поле


class Device {
   public: AnotherDevice device;
}

А внутри AnotherDevice еще одно поле с инстансом другого класса и т.д, и если будет такая цепочка из тысячи вложенных полей вырастет ли итоговый размер объекта InputDevice в сравнении вариантом когда строим цепочки через наследование или нет?

С++ аллоцирует память под композицию 1 раз, независимо от того, объект на стеке создан или в куче. В структуре объекта поле public: AnotherDevice device;
это не указатель, это сам объект AnotherDevice, все его поля. Компилятор знает, с какого смещения начинается вложенный объект и формирует указатели на вложенный объект, когда они нужны, добавлением константы к указателю на объект-композит.
А в с++ размер объекта будет строго равен сумме размеров объектов его полей или добавится еще небольшой оверхед на какие-то служебные поля? Получается что если есть даже минимальный оверхед на объект в один бит то с ростом цепочки вложенности будет расти и размер главного объекта а с наследованием длина цепочки переопределений на размер объекта не повлияет.
Сумма полей. К таковым, в данном случае, относятся укзатели на виртуальные функции — тоесть размер всех переменных-членов класса + vtable, а далеехоть так хоть этак определяй InputDevice — результат будет одинаковый.
Без виртуальных методов будет строго одинаково.
Для каждого включенного объекта с вирт. методами будет на 1 указатель больше (vptr).

Однако, если не наследоваться, виртуальные методы не нужны :D
НЛО прилетело и опубликовало эту надпись здесь
У меня есть своя теория на этот счет. В программировании полно народу, которые пришли сюда не потому что им это нравится, а потому что тут бабки платят. Соответственно среди таких полно даже гуманитариев. А гуманитарии не могут в абстракции, так как это по сути уже математика, уравнение в котором неизвестным является сразу все.
НЛО прилетело и опубликовало эту надпись здесь
Верстальщик это по сути junior JavaScript developer. Если нет, то верстальщик становится невостребованным на рынке. Гуманитариям приходится плакать, но продолжать жрать кактус. Плач выражается в хейте ООП.
Ковырните соседей по офису, запросто может обнаружится, что тот офигенный спец, который строит офигенный продукт, в девичестве имеет красный диплом по, скажем, экономике.
Не встречал таких. Физиков, математиков и конструкторов — встречал, экономистов — максимум тестеры или верстальщики (без особого JS), а вот программистов, не умеющих в «ться», встречаю постоянно.
НЛО прилетело и опубликовало эту надпись здесь
Видел, правда ещё не разобрался к какому классу относятся эти товарищи.При мне работали Барышни-погромистки в великой желтой программе, все сплошняком гуманитарии. И да, они таки писали.
А вы этой теорией за чей лагерь? «За ООП» или «против ООП»?)
Я за то, что все хорошо в меру.
Как то раз делал тестовое задание при приеме на работу. Нужно было написать демона, который пингует адрес и шлет репорты, если пинг не проходит. Там кода 10 строчек. Ну я и написал просто 10 строчек. Понятно, что для демонстрации скиллов этого не достаточно и я прикрутил демону ООПешную возможность прикручивать разные способы отправки репортов. Но все равно первые же два вопроса были «Почему не весь код ООП?», «Почему не использовали фреймворков?». Прикручивать какойнибудь симфони ради 10 строчек кода, да еще и демону?.. На работу меня тогда не взяли…

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


Вообще это такой суровый ынтырпрайз, не написал 2000 строк и всё через ООП вместо десяти которыми можно решить задачу. При этом перед тем как писать код ещё и UML диаграммы все нарисовать от use-case и заканчивая deployment. Это как дрескод нарушуть и в плавках на банкет явиться.

НЛО прилетело и опубликовало эту надпись здесь
  1. ООП смешивает данные и действия над ними. Это плохо

Думаю, Вы не совсем правильно поняли этот аргумент. Плохо когда пытаются абсолютно всё представлять объектами, искусственно симулируя наборы данных и действий там, где их нет: число 3 это не объект, равно как и функция sum(a,b int) не является частью/методом какого-то объекта. Всё хорошо в меру, где-то используемые кодом абстракции действительно лучше представлять в виде объектов, но требование чтобы объектом было абсолютно всё добавляет accidental complexity на пустом месте.


  1. Наследование закрепощает программу, делает трудным внесение изменений

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


  1. Методология ООП изначально ошибочна
    Абсолютно необоснованный аргумент. ООП создавалось для того, чтобы моделировать своеобразный виртуальный мир, состоящий из объектов, как и наш мир.

Наш мир не состоит из объектов. Это просто один из способов смотреть на мир, и способ довольно корявый и ограниченный. Иными словами эта абстракция не очень корректна, поэтому попытки применять её часто дают не очень хорошие результаты. А поскольку эта идея подаётся как основание ООП… Ничего не имею против ООП, но вот этот бред про "мир состоит из объектов" надо забыть как можно быстрее, он больше вредит ООП чем помогает.


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

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

Плохо когда пытаются абсолютно всё представлять объектами, искусственно симулируя наборы данных и действий там, где их нет

Это называется ООП головного мозга. Все хорошо в меру. Есть даже такой софистический прием — довести идею до обсурда, а потом на основе этого доказывать неверность идеи.
А когда его пытаются насильно внедрить ради формального следования DRY случается беда.

Тут даже из вашего примера следует, что проблема не в ООП, а в том что у когото DRY головного мозга. До меня однажды докапались, почему у меня дважды написаны 4 одинаковые строчки, мол я должен вынести все в функцию. Только вот каждая из этих строчек уже была функцией, но с разными параметрами. Вынесение в функцию вылилось бы в усложнение кода и даже в увеличение количества строк. Но человеку было все равно, донт репит ёрселф и все тут.
Наследование — это самый мощный инструмент ООП. По-сути, без наследования (и связанного с ним полиморфизма) ООП особого смысла не имеет, ибо все остальное можно смоделировать, назвав процедуры удобным тебе образом.

Тут надо отойти в сторонку и сказать, зачем нужны ООП/ФП/etc. Основная цель любой методологии и любого нормального языка программирования — упростить решение задач. Определенных задач, разумеется, потому что универсального подхода пока никто не придумал. В программировании простота решения коррелирует с количеством кода, которое ты должен написать в прикладной программе. Пишешь меньше кода — тратишь меньше времени, упрощаешь поддержку продукта. Именно с этой точки зрения надо смотреть на языки программирования. Категории «труъ» и «не-труъ» интересны только как индикаторы достижения цели.

Так вот, для упрощения работы программиста в незапамятные времена были придуманы библиотеки процедур и вообще структурное программирование. Сейчас апологеты композиции (composition over inheritance) говорят, что вам достаточно скомбинировать библиотечные функции нужным образом чтобы получить требуемое сложное поведение. Это так, но таковая композиция сама по себе является сложной сущностью. Аналог такой сущности — стойка с сетевым оборудованием. Все компоненты в стойке стандартны, но конкретная конфигурация стойки — нет.

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

Класс в ООП — это по сути композиция функций, в которой можно подменять отдельные компоненты. Для этого там существует наследование и полиморфизм. Все просто.

По-сути, без наследования (и связанного с ним полиморфизма) ООП особого смысла не имеет

Полиморфизм — безусловно правильная штука. Только вот к наследованию его привязали именно в ООП, а вообще полиморфизм отлично существует и без наследования. В качестве примера посмотрите на интерфейсы в Go или просто почитайте статью в википедии про варианты полиморфизма.

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

Ещё точнее — речь шла о наследовании как инструменте, а не о полиморфизме. Если наследование применяется не как инструмент для создания реальной иерархии, DRY и моделирования отношений is a между сущностями предметной области, а просто как синтаксическая форма записи необходимая для использования полиморфизма в конкретном языке (т.е. речь о создании абстрактных классов нужных исключительно для декларирования набора поддерживаемых методов, от которых потом выполняется множественное наследование не вкладывая в это никакого смысла кроме "я поддерживаю этот набор методов и подразумевающуюся с ним семантику") — в этом ничего плохого нет (как впрочем в этом нет и настоящего наследования чего либо).

Ну это просто одна из проблем, которую хорошо решают наследование+полиморфизм. Есть и другие, в том числе включающие наследование не только поведения, но и данных. Но в качестве контраргумента было достаточно одного примера.
НЛО прилетело и опубликовало эту надпись здесь
Наш мир не состоит из объектов.

Совершенно очевидно, что мир состоит из иммутабельных структур, а где-то рядом витают модули, в которых есть функции, позволяющие взять, например, иммутабельное яблоко и создать на его основе такое же, только без кожуры.
Совершенно очевидно, что мир состоит из иммутабельных структур
Назовете хотя бы одну иммутабильную структуру в реальном мире, не опускаясь до уровня элементарных частиц?

В примере с яблоком никакого нового объекта не создается, иначе достаточно было бы потыкать в яблоко зубочисткой, чтобы накормить всех голодающих в Африке.
Кажется, у вас детектор сарказма сломался…
Ну так тэга то не было, а по косвенным признакам не сработал — бывает…
Сарказм и отсылка к парадоксу Тесея
число 3 это не объект, равно как и функция sum(a,b int) не является частью/методом какого-то объекта.


А что такое число 3? Это «данные»? А что такое «данные»? Как «данные» могут существовать без интерпретатора?

Если я напишу вам сообщение «На столе три яблока», или «На столе 3 яблока», или «На столе III яблока», скорее всего, во всех трех случая вы представите себе подобное:


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

Объект — упаковка для смысла. Методы объекта — способ понимания смысла.
"Код на Smalltalk"

tableWithThreeApples := me interpret: 'На столе три яблока'

apple := tableWithThreeApples takeOneApple.
apple isFruit. "true"

numberOfApples := tableWithThreeApple howManyApplesDoYouHave.
numberOfApples = 2. "true"


Но если интерпретаторы разные, то и объекты, а значит и смысл могут быть разными. Вполне возможен такой поворот событий:
tableWithThreeApples := appleFanboy interpret: 'На столе три яблока'

apple := tableWithThreeApples takeOneApple.
apple isFruit. "false"
apple isComputer. "true"

numberOfApples := tableWithThreeApple howManyApplesDoYouHave.
numberOfApples = 2. "true"


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

Читая, хотелось аплодировать стоя — совершенно волшебная демагогия.


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

Расскажите это на уроке математики.

Ребенку, который не знает чисел и основ математики, будет непонятно число 3. Для такого ребенка в числе 3 нет смысла. Он не сможет интерпретировать сообщение «3 + 2», потому что не знает смысл объектов «3» и «2» и доступных у них методов вроде "+" и "-".

Хоть вы и не ответили на мои вопросы из прошлого сообщения, я все рано задам еще. Вы действительно думаете, что числа — это что-то не имеющее смысла? А если смысл всё-таки есть, то разве методы (операции, возможности) вроде "+", "-", "*", "/" существуют не для пояснения смысла, заключенного в число?
А если смысл всё-таки есть, то разве методы (операции, возможности) вроде "+", "-", "*", "/" существуют не для пояснения смысла, заключенного в число?

Только вы все напутали. +-*/ — это как раз объекты, а вот у них уже в свою очередь есть метод apply, который принимает в качестве аргументов числа: plus.apply(1,2)

Вопрос трактовки. Формально математические операции — это функции, а не объкты. Объекты имеют состояние, функции — нет. то есть не plus.apply(1,2), а plus(1,2). Или this = 1; plus(this, 2), для чего есть запись 1.plus(2) :)
Объекты имеют состояние

Могут иметь. А могут — не иметь.


Или this = 1; plus(this, 2), для чего есть запись 1.plus(2) :)

Тогда у каждого числа свой plus, что семантически неверно, операция-то только одна.

Тогда у каждого числа свой plus

С чего бы? Методы как бы общие для всего класса чисел.


Но дело-то не в них. Существует множество возможностей представить предметную область в виде набора объектов, и ни один из них не является заведомо правильным и семантичным.


Простейший такой способ — каждое число как объект. Другой способ — операция как объект.


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

С чего бы? Методы как бы общие для всего класса чисел.

Нет, в ООП вообще у каждого объекта свой метод. Просто часто они совпадают.


Первый случай больше подходит для создания собственных алгебр

Первый вообще ни в каких случаях не подходит.

В ООП вообще нет методов, с одной стороны, с другой, в тех реализациях, где они есть, чаще всего все методы одного класса делятся друг с другом, только значение указателя/ссылки типа this/self разное при вызове метода объекта, а код один.
В ООП вообще нет методов, с одной стороны

Нет, методы то везде есть, они только могут называться по-другому (сообщения, например).


чаще всего все методы одного класса

Методы не у класса, а у объекта. ООП ортогонально классам (на то оно и объектно-ориентированное, а не классо-ориентированное :)).
ООП с классами — это лишь один из вариантов.

Скорее методы очень частный случай сообщений, и это лишь один из вариантов :)
Скорее методы очень частный случай сообщений

Это не частный случай, а просто разные названия одного и того же :)

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

Ну и плюс собственно посылку сообщения можно реализовать через вызов метода.
Ну и плюс собственно посылку сообщения можно реализовать через вызов метода.

И наоборот. Именно по-этому это и есть одно и то же.

Везде есть функции. А вот диспетчеризации их вызовов могут быть разные. Это может быть прямой вызов конкретной функции (в том числе невиртуальные методы). Может быть выбор наиболее подходящей функции в зависимости от типа объекта (виртуальный метод), может быть выбор на основе всей сигнатуры (множественная диспетчеризация). Посылка сообщения — это вызов функции приёма сообщения с параметрами: объкт и сообщение. А как она диспетчеризируется зависит от языка.

Хорошо, «уговорили», у каждого объекта свой метод.

Но чем это плохо? Почему одной операции не может соответствовать целый класс методов?
Почему одной операции не может соответствовать целый класс методов?

А что это значит с точки зрения семантики? Мы же моделируем предметную область, так?

У каждого объекта реального мира своя реакция на события внешнего мира. Они могут быть одинаковы, например описываться одной формулой, но они принадлежат объекту.
Они могут быть одинаковы, например описываться одной формулой, но они принадлежат объекту.

Ну так операции над числами не принадлежат числам. Они не являются ни событиями, ни реакцией на эти события.

Это лишь условность. Захотим чтобы принадлежали — будут принадлежать.
Это лишь условность. Захотим чтобы принадлежали — будут принадлежать.

Так я же и спрашиваю — с точки зрения семантики предметной области это какой смысл имеет?
Так-то мы можем куда угодно засунуть какой угодно метод, например почему бы арифметическим операциям не быть методами уток? Или плюс — а утки, а умножение — у жирафа?
Надо что-то умножить? Отлично! Создаете жирафа с нужной шеей при помощи фабрики жирафов и умножаете! :)
Бессмысленно? Не более, чем иметь операцию плюс у числа.

Простите, а чем вас "сложи себя вот с этим числом и сообщи результат" в качестве семантики не устраивает-то?

а чем вас "сложи себя вот с этим числом и сообщи результат"

Тем, что числа не умеют складывать :)
Числа являются объектами арифметических операций, а не субъектами.

Умеют они себя складывать или нет — зависит только от построенной модели.
Умеют они себя складывать или нет — зависит только от построенной модели.

Так я ж говорю — ради бога, можете хоть жирафами умножать. Только это шизофрения, а так-то — норм все.

Если подразумевать под числом точку на прямой, то сложение это сдвиг заданной точки на заданный вектор. Это больше подходит для оператора '+=', но тем не менее смысл есть.
Зависит от того как моделируем. Сложение можно представить как сообщение числу «я хочу увеличить тебя на вот это число», а результат как «да, я себя увеличил, теперь у меня новое состояние».
Сложение можно представить как сообщение числу «я хочу увеличить тебя на вот это число», а результат как «да, я себя увеличил, теперь у меня новое состояние».

"Состояние у числа" — это уже совсем что-то шизофреническое, уж извините.

Только в рамках классической математики может выглядеть странно. Модель программы вовсе не должна следовать общепринятым математическим и прочим моделям.
Только в рамках классической математики может выглядеть странно.

Замечательно, я не против того, чтобы кто-то сделал свою неклассическую математику, но только тогда ваши числа — не числа.
А то, что для не-чисел может быть все не так как для чисел — это, конечно, верно.


Модель программы вовсе не должна следовать общепринятым математическим и прочим моделям.

Да, модель не обязана следовать чему бы то ни было, но обычно такая модель — говно, а не модель.

Так давно привыкли использовать модели, противоречащие математике.

Попробуйте отказаться от такого:
i=i+1
Попробуйте отказаться от такого:

  1. зачем?
  2. что тут чему противоречит?
зачем?

Потому что такая запись противоречит общепринятым математическим моделям, где изменение значения принято обозначать другой переменной (Ti, Ti+1, Ti+2 и т.д., вместо T, T, T).
что тут чему противоречит?
из i=i+1 следует противоречие 0=1
Потому что такая запись противоречит общепринятым математическим моделям

Почему противоречит? Нет.


из i=i+1 следует противоречие 0=1

Нет, не следует, потому что = — это присваивание, а не сравнение.

Но обозначается тем же символом. Выражаясь вашими словами — шизофрения это получается.
Но обозначается тем же символом.

Какая разница, каким символом оно обозначается? Важно не как вы что обозначаете, а какой эти символы несут смысл.
Шизофрения — это когда у вас что-то, что по смыслу должно быть числом, не ведет себя как число.
Обозначать же вы можете что угодно как угодно, и то, что в разных языках одни и те же вещи могут обозначаться по-разному (а формальный язык ZFC и язык какого-нибудь ЯП — это разные языки) не является чем-то странным.

Присваиваний в математике нет, есть введение обозначений (аналог let в функциональных языках).

Значение переменной не меняется. Иначе это не переменная, а функция, либо последовательность.
Вместо присваиваний в математике есть введение обозначений (аналог let в функциональных языках).

И что дальше? Вещи-то это разные. Где противоречие?


Значение переменной не меняется.

Вы же понимаете, что "переменная" в языке программирования и "переменная" в математике — это два совершенно разных объекта, которые просто называются одинаково?
Не надо подменять понятия и тогда никаких проблем у вас не будет. А иначе — да, повсюду одни противоречия начнутся. Если одним и тем же словом называть разные вещи и из этого пытаться какие-то следствия выводить.

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

В какой математике вы видели общепринятую модель с изменяющимися во времени переменными?
В какой математике вы видели общепринятую модель с изменяющимися во времени переменными?

Да в любой, которая описывает операционную семантику императивного языка.

А конкретнее? Чтобы была построена формальная система? Я знаю только одну модель, описывающую эволюцию переменных в памяти — машина тьюринга.
А конкретнее? Чтобы была построена формальная система?

Речь о семантике для языка.
https://ru.wikipedia.org/wiki/%D0%A1%D0%B5%D0%BC%D0%B0%D0%BD%D1%82%D0%B8%D0%BA%D0%B0_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)

Вы читали текст по ссылке? Он не даёт требуемых примеров:
В качестве инструментов построения таких моделей могут использоваться различные средства, например, математическая логика, λ-исчисление, теория множеств, теория категорий, теория моделей, универсальная алгебра.

И где в λ-исчислении (и т.д. по списку) модель императивного языка?

Вы б дальше текст по ссылке изволили прочесть, там где виды семантик. После этого ваши вопросы должны отпасть. В частности, нас особенно операционные интересуют:
https://en.wikipedia.org/wiki/Operational_semantics

Так «число ведёт себя» в математике и программировании совершенно разные вещи и вместе, и по отдельности «число», «ведёт» и «себя».

И? :)

Хорошая модель строится не по совокупности всех доступных знаний человечества, а по знаниям предметной области в данном в задаче контексте. Пример, спроси у бухгалтера что такое налоговая ставка, он скажет что это число, которое..., сейчас по умолчанию оно равно N. А потом скажет, что в некоторых случаях это число увеличивается до M.
Пример, спроси у бухгалтера что такое налоговая ставка, он скажет что это число, которое...

Не важно, что он скажет, важно, что он будет иметь ввиду. Мы ведь люди и общаемся на ЕЯ. Если вы будете такие слова бухгалтера буквально понимать, у вас, очевидно, проблемы.
В данном конкретном случае — я не думаю, что вы будете для decimal делать метод "увеличить_ставку".

Для Decimal наверное не буду, а вот для TaxRate extends Decimal сделаю метод «increase» именно для поддержки ЕЯ в предметно области

Но ведь это будет метод именно TaxRate, а не метод "числа" :)
Видите, вы верно поняли бухгалтера (не смотря на то, что "формально" он вроде как дичь сказал) и все в порядке.

TaxRate является числом. Грубо, есть натуральные числа, целые, дробные, десятичные и есть налоговые ставки. Ну а если бухгалтер про каждое число в программе будет так говорить то добавляю метод в Decimal, ЕЯ же.
Как по мне — композиция тут больше подходит. А вообще спор у вас странный, я потерял мысль( Можете, пожалуйста, прояснить предмет?
Для демонстрации того, что число может являться объектом со своим состоянием и методами, его изменяющими, наследование подходит лучше :) Собственно спор о том, является ли шизофренией представление числа в виде объекта, в том числе мутабельного.
А какая разница? Вот допустим, что не является. Или допустим, что является. Если одна из точек зрения верна, то следствие...?
Если моя точка зрения неверна, то можно ставить диагнозы по коду. В частности авторам всех языков, где всё объект или хотя бы есть объекты, представляющие числа. Ну и держаться от такого кода подальше, чтобы не заразиться шизофреническим мышлением :)
Ну а до того? Если подняться на уровень выше. Ну вот допустим ваш оппонент погарячился и не совсем так выразился и именно этот пример не так важен, но тот, изначальный родник, который привел нас к этой реке — о чем он?
Я так понимаю, речь про то, что Druu имеет ввиду числа как математические числа. То есть вечные константные инстансы, на которые ссылаются все математики мира. Тогда «изменение числа» это запись вида «2 := 10», что действительно странно.
Я так понимаю, речь про то, что Druu имеет ввиду числа как математические числа.

Правильно. Если же ваши числа — не математические, то это будет указано либо в семантике языка, либо будет что-то в виде TaxeRate из примера выше.

А почему для этой задачи не использовать персептрон? Он лучше, на ваш взгляд, справится?

Угу, но если я задам вопрос: "вы добавили метод к decimal?", то вы ведь ответите: "нет!", правильно? :)

НЛО прилетело и опубликовало эту надпись здесь

Более того, предлагаю подумать над такой темой, как сложение разных типов: 1 + 1.1 — метод какого класса должен быть вызван: левого операнда или правого? А если это должна быть внешняя функция, то как быть в случае сокрытия внутреннего состояния операндов? Можно попробовать воспользоваться более широкой областью видимости, но как быть в случае, если каждый класс раз разрабатывается совершенно разными людьми? Кто из них должен обеспечить совместимость с другими типами?

Вы действительно думаете, что числа — это что-то не имеющее смысла?

Нет, я такого не говорил. Я говорил, что число — это не объект. Смысл, как и методы, к числу 3 можно прилепить самые разнообразные (это вопрос упомянутой Вами интерпретации). А можно этого и не делать. Число 3 от такого отношения к нему не испортится, не сломается, не обидится, и не станет менее полезным. Это и отличает данные от объектов: у объекта есть "смысл"/интерпретация, пользуясь Вашей терминологией, а у данных этого нет. Да, к данным это можно добавить, и мы получим объект. Но делать это вовсе не обязательно.


А если смысл всё-таки есть, то разве методы (операции, возможности) вроде "+", "-", "*", "/" существуют не для пояснения смысла, заключенного в число?

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

Как только число 3 превращают в объект, к нему гвоздями прибивают какой-то один смысл

оно ограничивает наши возможности по интерпретированию числа 3 каким-то иным образом


Вам ничего не мешает добавлять любой смысл к объекту. Если в вашей ментальной модели число может быть кодом символа, отобразите это в числе:
charCode := 3 asCharCode.
charCode + 1. "Instance of CharCode didNotUnderstand #+"


Это и отличает данные от объектов: у объекта есть «смысл»/интерпретация, пользуясь Вашей терминологией, а у данных этого нет.

Пока это число 3, это просто данные, и мы можем наделять их любым смыслом


То есть данные — это все-таки что-то, что не имеет никакого смысла? Если число «3» и строка «лето» являются данными, значит ли это, что они по-сути равны как друг другу, так и абстрактному «ничего»?
Начали за здравие, закончили за упокой.

Из наличия собственного смысла у «трех яблок» никак не следует отсутствие собственного смысла у числа «3».
Конечно у числа есть собственный смысл, я это не отрицал. Я пытался донести идею о том, что числа — это не «данные», числа — это представление определенного смысла. А компактное представление смысла с возможностью исследовать и понимать смысл через использование доступных методов (операций, возможностей) — это объект.

Предлагаю отвлечься от программирования и подумать о том, как объяснить смысл чисел, например, дельфину.
Числа — данные, объекты — данные и методы их преобразования. Смыслом, как конкретным, так и абстрактным, и числа, и объекты наделяет человек. Дельфины вроде тоже что-то могут. :)
В первом сообщении я задавался вопросом, на который пока что никто не ответил :(
А что такое «данные»? Как «данные» могут существовать без интерпретатора?


Смыслом, как конкретным, так и абстрактным, и числа, и объекты наделяет человек.

Как сделать так, чтобы можно было передать смысл одного и того же объекта без искажений?
Как сделать так, чтобы можно было передать смысл одного и того же объекта без искажений?

Записать его определение.

В общем случае задача передачи смысла без искажений не имеет решения :( Каждый субъект передачи может иметь свои интерпретации смысла одних и тех же данных. Можно стремиться к тому, чтобы уменьшить различия, но полностью их устранить вряд ли получится.

С другой стороны, не всегда участников передачи интересует полное понимание смысла, достаточно чтобы соблюдались «правила игры».
А компактное представление смысла с возможностью исследовать и понимать смысл через использование доступных методов (операций, возможностей) — это объект.

У натурального числа только два доступных метода — это next() и isZero()

НЛО прилетело и опубликовало эту надпись здесь
Что характерно, например, в разработке веб-фронтенда такие хейтеры ООП спокойно используют HTML DOM и не жалуются, что это не кошерно. Возможно, даже не обращают внимание на факт соприкосновения с объектами.
Автор поднял правильную тему, и в правильном ключе. Спасибо.
Я, например, жалуюсь. DOM, как и HTML — то еще… чудо. HTML то нарушает еще одну известную парадигму, смешивая код (js) с интерфейсом (всё остальное). Но, другого просто нет, увы.
А вы не смешивайте, вот и все.
По моим впечатлениям, самые полярные и непримиримые мнения по функциональщине.
Плюсую, про ООП, кмк, все давно смирились с неизбежным
КМК эти мнения ничего не значат, функциональщина это чистый матан, кто с матаном не дружит, тот и функциональщину не осилит, процедуры и ООП понять проще, уровень абстракции меньше.
По моим впечатлениям, самые полярные и непримиримые мнения по функциональщине.

Ну не знаю, по-моему вполне однозначные мнения — высокий порог входа и хорошая пригодность для определенных задач.

Другая проблема, что сейчас много людей говнокодят на JS в процедурном стиле с элементами ФП и топят, что они — функциональщики, потому что отказались от классов.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Я не автор, но отношусь к этому сугубо отрицательно. Говорят, множественное наследование сложно, потому что может возникнуть «deadly diamond of death», но эту проблему можно элементарно решить на уровне компилятора. Если в классе возникает проблема неоднозначности выбора метода, то нужно заставить разработчика явно реализовать этот метод. Вот и все, вроде.

А какую проблему по вашему должно решать множественное наследование? От обычного-то обычно предлагается отказаться в пользу композиции, про множественное я и молчу. Если у вас объект и швец, и жнец, и игрец, то это уже полное нарушение SRP.

Представлять одну сущность с разных сторон. Яблоко, например, и фрукт, и шарообразное тело. Или квадрат — и ромб, и прямоугольник )))
Нет, примеров из реальности я могу привести. А вот примера из программистской жизни — не очень. Пару раз когда подобное требовалось это было вызвано дико кривой архитектурой.

И да, ничто не мешает иметь интерфейсы Ромб и Прямоугольник.

Наследовать интерфейсы — хорошо. Наследовать реализацию — не очень. Множественно наследовать реализацию — вообще страх.
И да, ничто не мешает иметь интерфейсы Ромб и Прямоугольник.
Но ведь это не поведение. Вот интерфейс ИмеетПлощать с методом ПосчитатьПлощадь — имеет смысл. Или «РисуемаяФигура» с методом «Нарисовать(холст)» Какой смысл вообще в «Ромбах» и «Прямоугольниках»?
Декларация того, что данный объект ведёт себя как ромб или прямоугольник, хотя вообще ими не является. Правда с трудом представляю, что бы это значило:)
Я лично рассматриваю интерфейс не как поведение, а как часть сущности. Например интерфейс IEnumerator — что-то, что умеет перечислять. Это не поведение, это существительное. Так же и ромб, и прямоугольник — это сущность, которая с некоторой точки зрения (i.e. при касте) является ромбом или прямоугольником.

Поведение это трейты. Похоже не интерфейсы, но немного другое все-таки. У них и названия другие. Интерфейсы — это IEnumrator, IList, ICollection… Трейты — Display/Debug/Clone/Copy/…
что-то, что умеет перечислять. Это не поведение, это существительное
Сначала говорите «умеет перечислять», а потом «существительное».

Конечно, названия у них существительные, но суть — поведенческая. ICollection/IList — содержит элементы и предоставляет определенное поведение. Коллекция вот, к примеру — имеет поведение добавления, удаления и получения элемента. Всё, что коллекция — должно иметь эти поведения. Понимаете о чем я?

Трейты — это вообще что-то из процедурного программирования. «Добавить такую фукциональность». Ну то есть интерфейс говорит «инстанс может содержать и работать с элементами», а трейт говорит: «добавь ка именно такую процедуру».
Не вижу смысла спорить об определениях. Суть не меняется, наследование реализации — признанный антипаттерн в большинстве случаев. Один из редких сценариев где это нужно — GUI, тут я не спорю. Множественное наследование куда злее. Его выкинули из кучи языков во время проектирования не из-за религиозных соображений или потому что кто-то не осилил бросать ошибку компиляции если метод неоднозначен.
Его выкинули из кучи языков во время проектирования не из-за религиозных соображений
А почему?
Ну например поэтому.
Там только религиозные соображения.
Multiple implementation inheritance injects a lot of complexity into the implementation. This complexity impacts casting, layout, dispatch, field access, serialization, identity comparisons, verifiability, reflection, generics, and probably lots of other places.

Это религиозные соображения?
Да, ничего конкретного нет.
Какие проблемы с casting?
Генерики вон тоже сложно, в Go их нет, однако они есть а Java, C#,…
Не знаю, я вопрос на таком уровне не изучал. Если бы я покопался в компиляторе пару лет и потом мне дали бы эту тему на ресерч на месяцок, я смог бы дать ответ.

Не вижу повода не верить человеку, который эту экспертизу провел.

Собственно с чего началась эта ветка:

Говорят, множественное наследование сложно, потому что может возникнуть «deadly diamond of death», но эту проблему можно элементарно решить на уровне компилятора.

1. Нет никаких доказательств, что это просто. Зато социальное доказательство обратного — есть
2. У людей нет задачи сделать идеальный компилятор, это невозможно, требуется сделать максимально хороший с ограничением по времени разработки. Если фичу сложно сделать, и она имеет малую ценность — нафиг её, даже если есть валидные сценарии и план резолва неопределенности.
Не вижу повода не верить человеку, который эту экспертизу провел.
Ну хотябы потому что нет ни одного конкретного примера.
Если фичу сложно сделать, и она имеет малую ценность — нафиг её,
Т.е. религиозные соображения
Скорее экономические соображения. Вот «малая ценность» можеь быть объявлена из «религиозных».
Поведение это трейты. Похоже не интерфейсы, но немного другое все-таки. У них и названия другие. Интерфейсы — это IEnumrator, IList, ICollection… Трейты — Display/Debug/Clone/Copy/…

Так трейты — это и есть множественное наследование. Если трейты ок — то и множественное наследование же ок?

Трейты — не наследование, они не реализуют отношение «является». По сути трейты чисто технический механизм переиспользования кода. Один и тот же трейт, например, может реализовывать разные интерфейсы, не связанные друг с другом наследованием.
Трейты — не наследование, они не реализуют отношение «является».

Реализуют, конечно, с чего бы нет?


По сути трейты чисто технический механизм переиспользования кода.

Ну, как и наследование :)


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

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


Смотрите, если вам показать множественное наследование и трейты и просить, в каком случае есть что, то вы не сможете определить без подсказки.
Т.к. с точки зрения семантики языка это одна и та же вещь. Нет никакого способа отличить реализацию трейта от наследника класса.
согласитесь, если что-то в точности ведет себя как наследование и неотличимо от него ни в каком контексте, то это наследование и есть.

Реализуют, конечно, с чего бы нет?

Зависит от реализации, конечно. В моём основном языке трейты не реализуют "являются", результат операции тип $obj instanceof TrateName всегда false.


Ну, как и наследование :)

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


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

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

В моём основном языке трейты не реализуют "являются", результат операции тип $obj instanceof TrateName всегда false.

Ну это неконсистентное поведение instaceof в вашем языке, не более того. Если класс реализует трейт, то он является соответствующим подтипом и то, что у вас instaceof не соответствует отношению подтипирования в языке — проблема, конечно.


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

А иерархии абстракций для чего создаются? ;)


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

Ну опять же это у вас в языке какие-то нестандартные трейты. Обычно они могут и интерфейсы имплементить и классы экстендить. Можете для примера в скале посмотреть, там наиболее канонiчная реализация трейтов (среди более-менее распространенных языков).


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

Класс не реализует трейт, он собирает себя из трейтов и собственных объявлений.

Ну или приведите своё каноничное определение трейта.
Класс не реализует трейт, он собирает себя из трейтов и собственных объявлений.

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


Ну или приведите своё каноничное определение трейта.

Трейт — это ровно тот же самый абстрактный класс, просто для его наследования зафиксирован более простой и ясный алгоритм выбора конкретного метода, когда у вас при наследовании получается конфликт имен. С-но реализация данного алгоритма выбора при наследовании — и есть "трейты".
Ниже было верно замечено, что обычно трейты не должны иметь состояний, но это несущественно, т.к. легко обходится, пусть и за счет несколько искусственных конструкций (трейт может расширять класс с состоянием).

трейты не должны иметь состояний, но это несущественно
Это существенно, т.к. самый главный аргумент против множественного наследования, которого нет в трейтах. Наследуясь от разных классов, нет возможности не забирать поля одного класса, если они уже есть в другом. С трейтами поля не дублируются никак.
т.к. легко обходится, пусть и за счет несколько искусственных конструкций (трейт может расширять класс с состоянием)
Вы хотите искусственно придумать себе граблей? Как сделать плохо, если нет множ. наследования, но есть трейты? Совсем запретить говнокод язык не может.
Можно узнать источник этого определения? Естественно каноничного, а не из спеки вашего любимого языка, потому что в моём любимом оно, мягко говоря, другое.
Наследуясь от разных классов, нет возможности не забирать поля одного класса, если они уже есть в другом.

Та же самая проблема возникает при наследовании методов.
Кроме того — любое поле можно заменить набором методов.


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

Если трейты точно так же позволяют наступать на грабли, то чем они отличаются от обычного множественного наследования?

Та же самая проблема возникает при наследовании методов.
Не возникает.
Кроме того — любое поле можно заменить набором методов.
Поле увеличивает sizeof объекта, увеличивая кол-во его возможных состояний, а метод — не увеличивает sizeof. Может, вы в это высказывание вкладываете какой-то особенный смысл, но я это прочитал как то, что вы нашли способ сжимать информацию без потерь до 0 байт.
Если трейты точно так же позволяют наступать на грабли, то чем они отличаются от обычного множественного наследования?
Так всё развитие языков программирования — это выдумывание всякого синтаксического сахара, чтобы быстрее писать код и меньше наступать на грабли. Трейты отличаются от обычного множественного наследования тем, что их массовое использование не приводит к типичных граблям, которые появляются при массовом использовании множественного наследования.
> Не возникает.

С чего бы вдруг? проблемы то все те же.

> Поле увеличивает sizeof объекта

При чем тут вообще sizeof?

> а метод — не увеличивает sizeof

Зависит от реализации. Может и увеличивать спокойно.

> Трейты отличаются от обычного множественного наследования тем, что их массовое использование не приводит к типичных граблям, которые появляются при массовом использовании множественного наследования.

Трейты — и есть множественное наследование, так что ваши слова бессмысленны.
Вы хотите сказать, что определенные правила разрешения конфликтов при множественном наследовании приводят к тому, что получается избежать некоторых типичных граблей? Это да.
Я хочу сказать, что множественно наследовать данные плохо, это приводит к свалке в объекте. А множественно наследовать методы — всего лишь синтаксический сахар. Если мы точно можем указать, какой метод вызывать, это всё равно, что методы лежат где-то отдельно и вызываются с аргументом — объектом.
Я хочу сказать, что множественно наследовать данные плохо, это приводит к свалке в объекте.

Так метод — это то же самое поле, в котором просто лежит в качестве данных ф-я. Если множественно наследовать данные плохо — то и методы множественно наследовать тоже плохо.

Так метод — это то же самое поле, в котором просто лежит в качестве данных ф-я
Зависит от реализации. Чаще я вижу методы как static-функции с this-аргументом. И для таких сред ваш аргумент «Если множественно наследовать данные плохо — то и методы множественно наследовать тоже плохо» не работает.
Зависит от реализации. Чаще я вижу методы как static-функции с this-аргументом.

Ну это то же самое, что я сказал.
Еще можно считать методы замыканиями относительно this. Не суть важно, в итоге одно и то же получается.

Вы как-то незаметно перешли от абстрактного ООП в вакууме к конкретной реализации в конкретном ЯП.

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

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


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

Так в теории ООП они одно и то же как раз.

Разницы между полями и методами как раз нет в конкретных реализациях, где методы обычные функции, а функции обычный тип данных, то есть по сути там где ООП построено на базе ФП. В «абстрактном» ООП это не так.
любое поле можно заменить набором методов

Нельзя. Иначе откуда методы будут брать значения полей? Состояние всегда будет где-то храниться.


Простой пример.


class C
{
    int n;

    int getN()
    {
        return this->n;
    }

    int setN(int v)
    {
        this->n = v;
    }
}

Попробуйте убрать поле n и заменить его набором методов.

Попробуйте убрать поле n и заменить его набором методов.

(getN, setN) = let n in (lamda () this->n, lamda (v) this->n = v)

Я не знаю, на каком это языке и что это означает. Но там явно есть this->n, значит никуда вы поле не убрали.

Я не знаю, на каком это языке и что это означает. Но там явно есть this->n

Ну это я скопипастил криво, должно быть просто n, а не this->n :)
Это n из контекста let.

Ага, я ждал такой комментарий. То, что вы переместили поле в глобальное адресное пространство, не означает, что вы его убрали. Часть глобального адресного пространства принадлежит объекту, как и раньше. Это ровно то же самое, что и публичное поле. А контекст в замыкании ведет себя точно так же как объект. JavaScript хороший тому пример.


Кроме того, разговор идет о наследовании, да еще и о множественном, а у вас нет класса C от которого можно было бы унаследоваться. А еще непонятно, как оно должно работать с несколькими объектами, это уже какое-то static поле получается, одно на всех.

Можно узнать источник этого определения?

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


потому что в моём любимом оно, мягко говоря, другое.

Чем? У вас instaceof сломанный, а с трейтами все ок.

Слово «канонический» не я ввёл в дискуссию. И, да, PHP один из самых распространённых языков и в нём трейты не реализуют отношения «является» или «реализует». Значит согласно вашему же определению, эти отношения не является общим для трейтов, а является деталью реализации в некоторых языках.
Слово «канонический» не я ввёл в дискуссию. И, да, PHP один из самых распространённых языков и в нём трейты не реализуют отношения «является» или «реализует».

Мне непонятно, почему вы говорите, что трейты в php не реализуют отношение "является", если на самом деле реализуют (иначе вы бы не могли вызывать на классе методы трейта, очевидно).
Аналогично и с "реализует" — поскольку в пхп-шных трейтах можно оверрайдить методы, то и "реализует" в том числе есть.

Потому что трейты в PHP несут чисто техническую роль, использование трейта в классе просто технически добавляет классу методы и поля как include, но в области видимости класса. Список трейтов, используемых классом, никак не влияет на то чем являются инстансы класса, от каких классов унаследовано и какие интерфейсы реализует. Никак внешне трейты себя не проявляют, используете вы трейт или скопипастите код из него — никакой разницы. Нет понятия «метод трейта», нельзя инстанс класс, использующего трейт, типизировать как инстанс трейта, как инстанс класса, его базвых классов, в том числе абстрактных, как инстанс интерфейса — можно, а как трейта нелья — это просто деталь реализации, не несущая никакой семантике. Один и тот же трейт может использоваться совершенно разными классами для реализации как собственных методов, так и методов никак не связанных абстрактных классов или интерфейсов.

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


Ну вообще можно выдернуть список используемых трейтов через рефлексию, иногда это даже удобно
Потому что трейты в PHP несут чисто техническую роль, использование трейта в классе просто технически добавляет классу методы и поля как include, но в области видимости класса.

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

Вам уже раз 15 сказали что нет отношения is a
Вам уже раз 15 сказали что нет отношения is a

Ну это же неправда. Я могу вызвать методы трейта, с-но отношение is a есть.

методы к is a не имеют никакого отношения. В родителе может вообще методов не быть, а is a будет
Можно проверить правилом подстановки.
Если в контексте, в котором можно использовать A, также допустимо использовать B, то есть отношение B is A.

Для трейтов эта проверка не выполняется.
Если в контексте, в котором можно использовать A, также допустимо использовать B, то есть отношение B is A.

Окей, А — трейт, В — соответствующий класс, приведите, пожалуйста, пример контекста, где можно использовать значение типа А, но нельзя использовать значение типа В.

У трейтов нет значений, т.е. с примерами на значения множество пустое.

Если же трейт A включен в класс B, то класс B, как правило, нельзя включить как трейт в класс C.
У трейтов нет значений, т.е. с примерами на значения множество пустое.

То есть утверждение "в любом контексте, где можно использовать значение типа А, можно использовать значение типа B" — истинно, так?


Если же трейт A включен в класс B, то класс B, как правило, нельзя включить как трейт в класс C.

Потому что он класс а не трейт. Естественно, класс нельзя включать как трейт, почему бы это должно было быть иначе?

То есть утверждение «в любом контексте, где можно использовать значение типа А, можно использовать значение типа B» — истинно, так?
Да. Та формулировка была для is-a у значений. У трейтов нет значений, значит, нужно проверять отношение is-a для типов, т.е. проверять, можно ли использовать имя класса в контексте имени трейта.
Естественно, класс нельзя включать как трейт, почему бы это должно было быть иначе?
Класс не является трейтом, и поэтому is-a не выполняется.
Методы есть, но они ничем для клиента класса не отличаются от ситуации, когда вместо использования трейта мы скопипастим методы из трейта. Ничем не будут отличаться методы трейт от методов класса.

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

Ничем не будут отличаться методы трейт от методов класса.

А с чего бы они должны отличаться? Это же наследование а не композиция :)

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

Отличаются только в том случае, если они в наследнике переопределены. Если не переопределены — то не отличаются. Аналогично и в трейтах — не переопределили метод => он не отличается.

Отличаются. В этом отличии чуть ли не суть наследования: На инстансе наследника можно вызвать метод родителя как на родителе.
Отличаются.

Отличаются чем? С точки зрения наследника все методы родителя — это его личные методы. Наследник, вообще говоря, даже не обязан знать о том, что он чей-то наследник.


На инстансе наследника можно вызвать метод родителя как на родителе.

Наследованные методы, которые вы вызываете, это методы наследника, а не методы родителя. Именно этим наследование отличается от композиции. При композиции у вас есть "предок" который лежит в отдельном поле, вы к нему обращаетесь и вызываете его методы. В случае наследования этого нет — любой наследник is-a родитель и любые методы, которые были у родителя, становятся его собственными.
Если родитель А обладает методом Х и наследник C is-a A, то С обладает методом Х, просто напрямую. Т.к. мы в утверждении "А обладает методом Х" просто можем заменить А на С.

Нет, не его. И наследник знает, что он чей-то наследник. В коде наследника нет какого-то метода, но он его активно использует через this. Но при условии, что этот метод не private, а если private то будет ошибка нарушения области видимости, а не несуществующего метода.

Ну и ещё есть ранее и позднее связывание, анализ которых подсказывает, что вызывается именно метод родительского класса, при раннем связывании прямо в контексте родителя, а при позднем — в контексте наследника. В коде родительского метода нужно явно указывать, что нужно вызвать не метод определенный в родителе, а что метод самого наследника. Разные есть нюансы. Но в случае наследования нельзя говорить о прямом обладании, это именно наследование.
Но в случае наследования нельзя говорить о прямом обладании, это именно наследование.

В этом случае наследование не задает отношения is-a.


С точки зрения наследника все наследуемые методы — это личные методы наследника. Т.к., еще раз, о том, что он наследник и вообще есть какие-то предки — наследник может и не знать в принципе.

Отношение is-a для внешних потребителей, внутренности инкапсулированы для них, но наследнику доступна информация о своём родителе через конструкции типа parent, с помощью которых наследник может вызвать метод родителя или ещё-что с ним сделать, если сам он переопределил.

Отношение is-a для внешних потребителей, внутренности инкапсулированы для них

Вы сейчас уже отсебятину какую-то выдумывает. Is-a это is-a, то есть любой потомок является своим предком. По-этому он обладает всеми методами предка как своими, в том числе.


но наследнику доступна информация о своём родителе через конструкции типа parent,

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

Нет, я пользуюсь вашим методом «наименьшего общего». В популярных ООП ЯП методы наследника и родителя не одно и то же. Модификатор private, позднее статические связывание, вызов метода прототипа в JS — всё говорит о том, что только для внешних систем реализуется отношение is-a, а внутри и наследник, и родители могут распознавать контексты. Именно с точки зрения семантики.
всё говорит о том, что только для внешних систем реализуется отношение is-a, а внутри и наследник, и родители могут распознавать контексты

Как?

По разному в разных языках. В PHP, например, по разному обрабатываются self::method(), parent::method() и static::method()
Вот и попробуйте сделать в том же php:
class Foo {
    use TraitBar;
}

$foo = new Foo;
$foo instanceof TraitBar


И внезапно окажется что даже там трейты не подходят под отношении is a

PS: Ну и в php трейты — не совсем трейты
Действительно, такая фича может и отличаться от наследования, но если говорить о трейтах не в контексте конкретной реализации в конкретном языке, то от наследования они не отличаются.
У трейтов существенное ограничение относительно множественного наследования — они не могут включать в себя состояние, т.е. добавлять к классу переменные.

Благодаря этому пропадают вопросы к наследованию, такие как: если объект наследуется сразу от «прямоугольника» и «квадрата», то у него 3 размера — длина, ширина и сторона? Нет, у объекта можно определить один габарит, и длину-ширину трейта «прямоугольника», как и сторону трейта «квадрата» замапить на него.

Особенно хорошо этот принцип работает для сложных иерархий: если Button и EditBox — это GUI-объект, то, если отнаследовать свой контрол от обоих, у каждого всё равно останется свой собственный parent? С трейтами parent будет единственный, определённый в базовом классе.
Множественное наследование без реализации — конечно ок.
Множественное наследование без реализации — конечно ок.

Но трейты — это множественное наследование с реализацией. Без реализации — это интерфейсы.

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

Переопределять что?


А при явном указании трейта к которому надо привести не возникает неоднозначности.

Никто не мешает сделать так же наследования классов.

Переопределять что?

Методы
Никто не мешает сделать так же наследования классов.

Проблема с наследованием состояния еще и в том, что непонятно, должно мы использовать одно и то же поле, или разные.

Допустим, у нас есть трейты Foo и Bar, и нам разрешено в них хранить состояние. У них есть поле count. Мы реализуем эти трейты для какого-то типа. Должны ли мы сделать два поля count?
Методы

А интерфейсы как позволяют методы переопределять?


Проблема с наследованием состояния еще и в том, что непонятно, должно мы использовать одно и то же поле, или разные.

Любой метод является полем. В случае наследования методов мы одно поле должны использовать или разные?


Допустим, у нас есть трейты Foo и Bar, и нам разрешено в них хранить состояние. У них есть поле count.

Допустим, у нас есть два трейта Foo и Bar с методом count(). Мы должны сделать два метода count()?

Любой метод является полем.

Только в ваших фантазиях.

Только в ваших фантазиях.

А чем они, по вашему, отличаются?

Поле, которое можно перезаписать — это состояние и это дополнительная сложность.

Если метод реализован как поле, но не меняется (по соглашениям кодирования), то проблемы он не вызывает при наследовании.
Если метод реализован как поле, но не меняется (по соглашениям кодирования), то проблемы он не вызывает при наследовании.

Ну а если метод внутри обращается к некоторому полю, разному, в зависимости от реализации? Та же проблема в итоге будет.

Метод из трейта не может обращаться к полю напрямую, т.к. в коде трейта поле недоступно. А обращаться через метод-геттер — пожалуйста, с учётом того, что под каждый трейт можно написать свой геттер/сеттер, учитывающий логику трейта.
Метод из трейта не может обращаться к полю напрямую, т.к. в коде трейта поле недоступно.

Он может обращаться к полю parent-класса.

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

Ну да, она не возникает, потому что в трейтах при наследовании правила другие и поля не будут копироваться. И?


Смотрите, у вас тот же самый ромб — есть класс А с полем n, есть два трейта Foo и Bar, с методами, использующими это поле, есть класс Yoba extends A with Foo with Bar.
Есть две стратегии разрешения этого ромба — либо раскопировать поле n, либо оставить его одно. В обычном наследовании применяется первая стратегия, в трейтах — вторая.

Разница в том, что при множественном наследовании нет вариантов оставить одно поле (виртуальное наследование не рассматриваем по соображениям производительности).
А интерфейсы как позволяют методы переопределять?

Напрямую не позволяют (т.к. могут только перекрывать), но в производных классах вполне себе переопределяются. У трейтов же нет понятия «производных классов», потому что нет наследования. И этой проблемы тоже нет.

Любой метод является полем. В случае наследования методов мы одно поле должны использовать или разные?

Не является, хотя бы с точки зрения потребляемой памяти. Каждое поле клонируется каждым инстансом объекта. Каждый метод является разделяемым и хранится в единственном экземпляре. Сгенерировать лишний метод может быть позволительно, удваивать-утраивать размер объекта — нет.

Допустим, у нас есть два трейта Foo и Bar с методом count(). Мы должны сделать два метода count()?

Да. Сразу говорю, что «ну тогда должны сделать 2 поля count» — не работает, ибо см. выше.
У трейтов же нет понятия «производных классов», потому что нет наследования.

Есть, и методы там переопределяются.


Не является, хотя бы с точки зрения потребляемой памяти.

С точки зрения памяти — может и нет, а с точки зрения семантики — очень даже да.


Да. Сразу говорю, что «ну тогда должны сделать 2 поля count» — не работает, ибо см. выше.

Что см. выше? В случае двух методов те же проблемы будут с памятью. Пусть, например, эти методы — замыкания.

Есть, и методы там переопределяются.

Пример кода?
С точки зрения памяти — может и нет, а с точки зрения семантики — очень даже да.

Если факты не укладываются в теорию — тем хуже для фактов? Язык используется для написания прикладных программ, а не только для семантической эстетики. В языки часто добавляют детали, смысл которых отсутствует с точки зрения семантики (атрибут inline, например), но которые важны для производительности.

С некоторой точки зрения поле можно представить как пару методов, но нам эта точка зрения в данном случае неинтересна.
Пример кода?
trait yoba {
def foo {...}
}
class bar extends yoba {
override def foo {...}
}


Если факты не укладываются в теорию — тем хуже для фактов?

Какие факты? Если что-то крякает как утка, плавает как утка и выглядит как утка — наверное, это утка. Как раз на основании наблюдаемых фактов.


С некоторой точки зрения поле можно представить как пару методов, но нам эта точка зрения в данном случае неинтересна.

Правильно. По-этому нет никакого смысла вообще разделять поля и методы (на самом-то деле разница в семантике обычно есть, связанная с рантаймом языка, но для данного конкретного обсуждения она неинтересна и несущественна).

trait yoba {
def foo {...}
}
class bar extends yoba {
override def foo {...}
}

Окей, допустим. В этом соглашусь.


Правильно. По-этому нет никакого смысла вообще разделять поля и методы (на самом-то деле разница в семантике обычно есть, связанная с рантаймом языка, но для данного конкретного обсуждения она неинтересна и несущественна).

Конечно имеет смысл, потому что понятия ортогональные. Если в языке вообще НЕТ концепции поля, то иметь какой-либо изменяемый стейт программа не сможет.

Если в языке вообще НЕТ концепции поля, то иметь какой-либо изменяемый стейт программа не сможет.

Может, в замыкании.

Замыкание на переменную со стейтом и есть метод, который записывает данные в поле. В данном случае поле оформленно как замыкание на «типа» локальную переменную.
В данном случае поле оформленно как замыкание на «типа» локальную переменную.

Любое поле оформлено как пара методов, да. Именно это я и пытался доказать :)

Эта пара методов не из воздуха берет значение, и не записывает его вникуда, а писать должно в память. В конечном итоге где-то есть постоянное хранилище (aka поле), в которое идет запись.

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

Методы хранить дешево, объекты — дорого. Хотя бы по этой причине наследование структуры базового (базовых?) класса(ов?) — плохо.
Грубо говоря, поле — это уникальная область памяти, используемая этой парой методов.

"область памяти" — это низкоуровневое понятие. На том уровне, на котором существует это понятие, нету никаких полей.


Методы хранить дешево, объекты — дорого.

Мы выяснили, что метод — это замыкание.
Вопрос на засыпку — что дешевле хранить: объект или замыкание? ;)

«область памяти» — это низкоуровневое понятие. На том уровне, на котором существует это понятие, нету никаких полей.

Конечно существует. Компилятор как раз такая сущность, которая сопоставляет эти вещи. Раз он может, то и мы можем.


Мы выяснили, что метод — это замыкание.

"мы" это не выяснили. Замыкание может иметь дополнительные данные, метод — нет. Грубо говоря, замыкание это (method, data), а метод это просто method. Так вот второй элемент кортежа — дорогой.

"мы" это не выяснили.

Как это нет?


Замыкание на переменную со стейтом и есть метод
Можно в этом всём разобраться, если вспомнить концепцию делегатов из delphi или c#.

Делегат позволяет вызвать метод объекта без объекта, т.к. сам состоит из двух указателей — на объект и на адрес функции метода.

В дискуссии перепутаны методы из классических ОО ЯП, в которых метод — это атрибут класса (указатель на адрес функции), и не может быть вызван без экземпляра объекта (статические методы не рассматриваем) и «методы» языков типа js, к которых ООП реализовано иначе, а именно замыканиями (которые в c# реализуются делегатами, не являясь методами).

Создавая замыкание (или «метод» в js), фактически создаётся новый объект (безымянный), который может хранить состояние.
В данном случае поле оформленно как замыкание на «типа» локальную переменную.
Любое поле оформлено как пара методов, да.

Нет. Вы заменяете переменную-член и пару методов на другую переменную и пару других методов. Это, очевидно, эквивалентные наборы, и они не эквиваленты наборам «переменная» или «пара методов».
Вы заменяете переменную-член и пару методов на другую переменную и пару других методов.

Все верно, возможность такой замены — это именно то что я с самого начала и хотел показать.

Нет, вы утверждали, что переменная заменяется на пару методов. А это не так, переменную можно заменить только другой переменной.
А это не так, переменную можно заменить только другой переменной.

Вы путаете переменную и поле. Поле можно заменить парой методов и переменной (локальной, не полем), на которую будет сделано замыкание. Очевидно, что здесь использование переменной не должно являться чем-то дополнительным, т.к. это странно запрещать использовать локальные переменные в данном контексте.

Опять нет) Поле можно заменить переменной. Точка. Если поле было публичное, то никаких 2 методов не надо. Если непубличное, и к нему прилагались геттер и сеттер, то нужна другая пара методов, которая заменит эти геттер и сеттер, а не поле.

В данном контексте странно умалчивать о наличии хранилища в правой части утверждения, так как в левой части речь идет о нем.

Ну ок, теперь понятно, только непонятно, что вы этим хотели доказать. Как это убирает проблемы множественного наследования? Как я понимаю, при такой схеме оно просто невозможно, но для его запрещения есть более простые средства.
На одном из форумов в интернете была замечательная мысль:
«Не понимаю, почему они выбирают между OOP и процедурами? Можно же выбирать между OOP и HTTP»
Сколько не пользуюсь ООП, все равно не уверен, что делаю это правильно.
Идеи ООП описаны для некого абстрактного понятия объекта (класса), но объекты бывают очень разные.
Например, доменные классы. Как их лучше описывать в ООП? Это просто иерархия структур данных или у них могут быть методы? Сколько раз пытался добавить к доменным классам поведение, каждый раз получалось плохо. Сторонние статические хелперы описывались гораздо проще.
Красивее всего с ООП получается писать нечто вроде графических редакторов. Там все как по книжке: базовые классы задают общую концепцию системы, затем они расширяются разными способами и реализуют все необходимое многобразие функций.
Самой загадочной и сложной для меня областью применения ООП являются всякого рода обрабатывающие информацию сервисы, которые должны принимать запросы, следить за изменениями внешних данных, анализировать, вычислять, отправлять ответы, формировать некие события, воздействия и т.д. Программа все время пытается превратиться в ком из каких-то менеджеров неопределенного назначения, тесно взаимодействующих с нарушением инкапсуляции и внутри построенных по сути на обычной процедурной концепции. Если в таких задачах попытаться создать более мелкие объекты и решать задачи за счет их взаимодействия, то для меня вообще все превращается в сущий ад.
Было бы очень интересно где-то прочитать про best practices, особенно по последней задаче.
Не нужно гнаться за «объективизацией», как за самоцелью. Применяйте, если это делает ваш код короче (и проще в поддержке). Большое приложение может состоять из независимых кусков — сервисов, каждый из которых предоставляет OOP-like интерфейс для внешнего мира, но не обязан быть объектно-ориентированным внутри. Также нет никаких требований объединять все классы приложения в единую мега-иерархию.
Не нужно гнаться за «объективизацией», как за самоцелью. Применяйте, если это делает ваш код короче (и проще в поддержке).
Это разумный подход. Но всегда найдется программист Java, который скажет что ООП отлично подходит для задачи, просто «вы не умеете его готовить».

Приходит Java-прогер в столовую и говорит: приготовьте мне oop OOP = new OOP();

OOP oop = new OOP();

Не бывать мне java-программистом:D

Красивее всего с ООП получается писать нечто вроде графических редакторов.
это пока вы не реализуете Undo/Redo и банальное сохранение.
Думаю, если поведение undo/redo/save и поведение при редактировании инкапсулировать в объекты, то действительно все станет плохо. Во всяком случае, мне такие вещи не удаются.
Я думаю проблема в том, что undo/redo/save — это более низкий уровень абстракции по сравнению с основными функциями объекта в графическом редакторе. А поведение при редактировании — это общее поведение группы объектов а не отдельного объекта.
Лично я для undo/redo/save делал объекты не plain и все изменения проводил через отдельный менеджер. Поведение при редактировании реализовывал отдельными классами-обработчиками.
ООП в классическом виде хорошо работает для задач типа отображения на различных устройствах с общим базовым классом различных графических объектов, так же имеющих общего предка и прочие функции того же уровня абстракции.
А в чем с ними проблема?
Undo/Redo типично реализуются через паттерн Команда, который существует как раз благодаря ООП. В чем проблема то?
Ну, большинство ООП-паттернов создано чтобы обойти ограничения ООП :) Так что «паттерн существует благодаря ООП» звучит как «полиция существует благодаря преступникам». Причинно следственная связь отражена вроде верно, но есть нюансы.
Вообще насколько я понимаю паттерны появились естественным путем, через выделение часто используемых конструкций для решения задач определенного вида.
Именно. В языке конструкции не предусмотрено для решения этой задачи, а хорошее решение из нескольких конструкций не очевидно.
Этот паттерн предполагает, что поведение графических объектов при редактировании вынесено во внешние классы, а сами графические объекты работают просто как структуры данных, изменяемые внешним алгоритмом на основе команд.
При наивном восприятии идеи ООП объекты должны сами знать как им реагировать на те или иные ситуации при редактировании. В этом случае команды могут быть не обратимыми.
Простой пример: мы двигаем в редакторе элемент, который связан с другим элементом и они должны двигаться вместе. Команда знает о первом объекте, но не знает что попутно сдвинула и второй. Тогда undo команды может работать не правильно. Значит правила поведения при редактировании нельзя инкапсулировать в объекты.
Не предполагает он вынесения поведения во внешние классы. Более того, он служит ещё большей инкупсуляции поведения, когда инвокер даже не знает какой ресивер будет обрабатывать команду, не гоаоря о том, как ей обрабатывать.

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

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

Ну зачем, расскажите мне, городить башню, где классы Муха и Котлета наследуются от суперкласса Сыр, который, в свою очередь, наследуется от суперкласса Пятница?!

Вот именно поэтому ООП и критикуют, потому что в реальном мире реализации скрыты и миллионы лет развиваются параллельно совершенно идентичные механизмы, а видим мы в реальном мире только использование: взяли котлету и запустили в стену, взяли стакан и — туда же, взяли самолётик и тоже — запустили, нам говорят — наследуйтесь, оно всё летает!
По факту никакие иерархии невозможно перенести с реальной жизни, их просто нет, а классифицировать использование иерархией не возможно, потому что любую вещь можо использовать по разному, все использования существуют одновременно. Иерархия наследования и реальный мир, это изначально ложный тезис. Выход в том, что наследование только для переиспользования кода, но если ты его уже знаешь, то можешь поправить, а если не знаешь, то не сможешь понять от чего наследоваться.
Не знаю, где и что там Вам говорят, но у нас тут наследование используется как инструмент для реализации run-time полиморфизма. И никто ничего из реального мира не переносит, хоть результат зачастую похож на реальный мир. И то, что у любой вещи есть множество использований, Вас тоже не должно волновать — сосредоточьтесь на том использовании, которое нужно Вам.
нам говорят — наследуйтесь, оно всё летает!
Кто говорит?
нам говорят — наследуйтесь, оно всё летает

Конечно, это ведь экземпляры класса PhysicalObject, у которого есть поведение полёта. Если вы моделируете полет объектов реального мира, то и котлета, и самолет будут иметь одного и того же «родителя» с различием, видимо, в геометрии.
Наследование закрепощает программу, делает трудным внесение изменений

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


Философия ООП гласит: правильно организованный и инкапсулированный класс можно рассматривать как чёрный ящик.

Любой библиотечный код (начиная от соседнего файла) можно и нужно рассматривать как чёрный ящик, и ООП тут, в общем-то, не при чем. Никто не полезет в ваш код, пока он работает как надо. Over-engineered система сокрытия это удел в основном c++ и потомков (хотя хватило бы public и protected (по умолчанию)).


В реальном мире нет иерархий отношения, повсюду лишь иерархии включения

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

А еще есть ощущение, что многие ставят знак равенства между «ООП вообще» и «моделированием доменной модели через ООП». Это совсем не одно и тоже. Как правило, построить доменную модель на объекта действительно очень сложно. И редко нужно.
Или точнее, знак равенства между описанием доменной модели и описанием поведения объектов доменной модели. Само описание доменной модели на ООП отлично работает. Пример: Common Information Model. Что касается описания на ООП поведения доменных объектов — с трудом представляю когда это может быть оправдано. Для одной задачи нужно одно поведение, для другой другое и непонятно как разделить общее по сути поведение между взаимодействующими классами.
Для одной задачи нужно одно поведение, для другой другое и непонятно как разделить общее по сути поведение между взаимодействующими классами

Как вариант — иметь разные объекты/классы для разных задач (контекстов).
Почти все языки программирования являются тьюринг-полными, за исключением языков разметок, как то: HTML, XML, CSS

CSS3 является тьюринг-полным. На нём реализовывали Rule 110, который в свою очередь является тьюринг-полным клеточным автоматом.
Где блин такие радикалы находится?! Ни одного не встречал.
В жизни встречал холивар между шарпом и жабой, между статикой и динамикой. Но чтоб открещиваться от какой-то из парадигм? Да ни в жизнь!
Я больше за то чтоб в яп было доступно и то и другое и можно без хлеба.
Да полно их! Посмотрите хотя бы на голосовалку за эту статью. Я также заметил, что у автора вначале карма была ненулевая.
Минус в карму автору статьи еще не означает что минусанувший — анти-ООПешный радикал. Кто-то мог счесть пост переливанием из пустого в порожнее. Или аргументы могли показаться слишком слабыми. Или же приведенные аргументы противников могли показаться «соломенным чучелом». Или просто форматирование не понравилось. Или все вместе взятое.
НЛО прилетело и опубликовало эту надпись здесь
Видимо, логическое программирование.

Лично для меня этот спор остался в далеком прошлом.
С приходом мультипарадигмальных ЯП, таких как Scala, C#, можно выбрать лучший подход по ситуации.
Но выбор не должен диктоваться личными предпочтениями. Там где есть большое и сложное состояние в памяти, там где данные первичны, лучше подойдет ООП, а там, где первичны алгоритмы, функции, многопоточность лучше выбрать ФП подход.
Именно этим и обусловлены рост и падение популярности разных подходов в разное время. В 90-е были популярные десктопные приложения. А это, в первую очередь, сложное состояние, которое декомпозировали, инкапсулировали, выстраивали предметную область из этих абстракций.
С приходом популярности веб приложений и HTTP протокола, приоритеты поменялись. Приложения стали выстраиваться как конвейер по обработки запросов, с внешним хранилищем состояния и популярность ФП стала расти.
Интересно к этом делу подошел ReactJS, где оба подхода совмещаются.

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

Безусловно. ООП как средство сокрытия данных нужно именно тогда, когда данные сложны и слабо формализированны. Это то, о чем я говорю, но я, видимо, не очень ясно выразил свою довольно абстрактную мысль. Вот вам примеры.


Пример 1.
Мы пишем микросервис, без фреймворков. Для этого нам нужно:
1 открыть сокет на прослушивание и переадресовать клиента в обработчик
2 считать запрос в буфер
3 распарсить его
4 сделать ему валидацию
5 выполнить запрос к хранилищу
6 отправить ответ
Требования к сервису: многопоточность, надежность.


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


Пример 2.
Пишем десктопную корпоративную систему учета чего-то-там, пусть будет товара. В ТЗ у нас есть пользователи, права, виды товара, катерогии, группы, отчеты, загрузка/выгрузка данных, цены, скидки, контрагенты и еще 1000 других сущностей. Сущности могут вести себя по разному в зависимости от состояния. Все это хаотично взаимодействует с сервером, интерфейсом, файловой системой, БД, туда подключают сканер по COM порту…
Здесь предметная область сложная, запутанная, может не поддаваться строгой формализации, часто меняться. В этом основная сложность, и тут принципы ООП как раз могут помочь.

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

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


ФП не поможет с многопоточностью

Вообще то может помочь, иногда. Нам не обязательно ждать завершения считывания данных из сокета, чтобы начать парсинг. Это называется параллелизм данных. Хотя это не относится ни к ФП, ни к ОПП, мне кажется что такую фичу легче будет реализовать используя ФП подход, хотя бы потому что это требует конечного автомата (но это сильное заявление, проверять мы его, конечно же, не будем :) )


лучший вариант — реализовать саму функцию последовательно и запускать несколько демонов для ее выполнения для разных запросов.

Обычно, оно так и происходит. Только демоном называют отдельный процесс, а у нас это пул потоков. Но если мы хотим асинхронный сервер, то можно выстроить ф-ии в цепочки promis'ов, тут ФП тоже будет кстати.

Тут может помочь иммутабельность, например.
Иммутабельность чего, чем помочь?
Нам не обязательно ждать завершения считывания данных из сокета, чтобы начать парсинг.
Мой опыт с ФП весьма ограничен, но я вот вообще не понимаю, как на нем такое вот организовать ( мы ведь возвращаем данные из функции целым куском, а не по частям ). А вот на императивном — запросто.
Но если мы хотим асинхронный сервер, то можно выстроить ф-ии в цепочки promis'ов, тут ФП тоже будет кстати.
Насколько я понимаю, в зависимости от тяжести подзадач, это может иметь как позитивный, так и негативный эффект ( из-за оверхеда task-системы ). А распаралеливание по демонам при большом количестве запросов и железо загрузит, и оверхед сведет к минимуму.
ФП, конечно же, не поможет параллельно исполнять ф-ии зависимые друг от друга, речь шла о параллелизации исполнения нескольких запросов от клиента, а не шагов по обработке запроса. Тут может помочь иммутабельность, например.
Затык все-равно будет в базе, а параллелить несколько запросов от клиента можно на любой парадигме
Ваш первый пример ничем не отличается от объекта в ООП.

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

http get: 'microservice-1.com/apples-count'.
http get: 'microservice-2.com/apples-count'.

object1 applesCount.
object2 applesCount.
НЛО прилетело и опубликовало эту надпись здесь
Разве ООП запрещает иммутабельные структуры данных, чистые функции, функции как first class citizens?

Не запрещает, но и не запрещает не использовать их тоже


Разве ФП запрещает абстракцию, инкапсуляцию, наследование и виртуальный полиморфизм?

Не запрещают, просто не поддерживают в удобном виде.
Чувствуете разницу?
Как я уже написал, есть мультипарадигмальне ЯП, где можно выбрать необходимый "арсенал" возможностей из ФП и ООП по ситуации для каждого компонента системы и они не будут противоречить друг другу, как вы и написали.


Кстати, там, где данные первичны, лучше подойдут базы данных :)

Может быть я не понял вас, но это неуместный сарказм. Вы же не будете писать сайт на SQL Sever, embdeded прошивку на Oracle, а 3Д игру на Postgres. Речь идет только лишь о ООП и ФП

НЛО прилетело и опубликовало эту надпись здесь
Забавно, что автор не увидел основные проблемы ООП. Это

  1. Качество иерархии. Для одной и той же предметной области разные люди придумывают разные иерархии. И если иерархия придумана плохо — мы получаем кучу friendly и ООПшное спагетти. Конечно, нерешаемых проблем нет и программист высокого класса может для любой задачи придумать хорошую иерархию. Но средних программистов больше, чем хороших.
  2. Отсюда вторая проблема — порог входа. Чтобы написать ООПшный код, который не рухнет за 2-0 лет развития, нудно быть намного более сильным проектировщиком, чем в случае процедурного кода.
  3. Следующая проблема — соответствие предметной области. Есть области, где иерархия естественна. Прежде всего это всякие GUI-библиотеки. А есть области, где иерархию нужно вымучивать, натягивая сову на глобус. В итоге все объекты — солитоны, а от ООП — одно название. Ну и оверхед, который в данной ситуации лишний.
  4. Ещё одна проблема — методология проектирования. В ООП принято, что мы делаем кирпичики, потом из них нечто собираем. Причем кирпичики делаются один раз, а используются — много раз. Для многих предметных областей это так. Но не для всех. В итоге — опять оверхед, и прежде всего — по времени разработки.

А основная причина флейма — качество преподавания ООП. Никто уже не бреется топорами и не забивает ими гвозди, хотя хорошим топором можно делать и то и другое. Поэтом и нет флейма, что бриться удобнее бритвой, а гвозди забивать молотком.

И лишь ООП преподают как религию, как единственный инструмент для всего. :-( Отсюда и толпы бывших студентиков, считающих ООП панацеей, а С++ венцом творения в любых областях. А где сектанты — там и флейм с ними.
1. Проблема людей, а не ООП. Средний программист при любом подходе выдаст средний результат, на то программист и средний.
2. То, что между ООП и процедурным кодом запросто проводится однозначное соответствие, конечно, никак не отменяет сказанное Вами. (sarcasm)
3. Опять же, натягивание совы на глобус — это проблема людей. ООП — инструмент, и есть задачи, где он подходит не лучше всех, но из-за этого не стоит выбрасывать его на помойку, что и пытается донести автор статьи. И в общем случае, оверхеда от использования ООП, ясное дело, нет.
4. Ничто никем нигде не принято. Хотите — пишите сначала высокоуровневую логику, а потом — низкоуровневые детальки. Оверхеда по сравнению с чем-либо другим, опять же, не видно.

Качество преподавания ООП, ИМХО, не хуже чем качество преподавания чего-либо другого :) Религию разводят, опять же, люди; личное мнение студентов-сектантов — проблема студентов-сектантов.
Согласен с вами, коллега. ООП — хорош, но не для людей. Вот создадим сильный ИИ — тогда ООП и покажет свою мощь. :-)

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

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

Вы рассуждайте в терминах денег — сразу увидите оверхед. То есть вопрос ставится так — сколько нужно заплатить, чтобы программа была написана к такому-то сроку. И тут разница может быть в 2-3 раза. Как в пользу ООП, так и наоборот. Вот эти лишние деньги, пошедшие на оплату ООПшных красивостей — и есть оверхед.

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

Нельзя рассматривать стиль программирования в отрыве от людей, его использующих. И в отрыве от задачи. Это единая система для минимизации затрат — люди + задача + стиль.
Есть и такой вариант — опытный программист делает иерерхию, а остальные ее используют. В этой ситуации большинство людей в команде тратят существенно меньше времени, потому что следуют уже заданным шаблонам, а не лепят свой велосипед. К тому же, код у всех получается более-менее одинаковый, что упрощает поддержку продуктов командой.
Хороший вариант, если такой человек есть. Вопрос лишь в том, выгодно ли его иметь в команде?

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

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

А ничего, что С++ — это надмножество С

Линус об этом и пишет. «Вы можете писать на С++, но для этого вам придется скатиться к С-подмножеству».
Т.е. прощай деструкторы и вместе с ними вся стандартная библиотека. Вам оно надо?
Чего это вдруг?
Исключения можно отключить (так и делают почти все, кому есть дело до быстродействия), про уровни абстракции и неявное управление памятью вообще не понял, где они. Про эмоции — если кто-то говорит с такими вот эмоциями, то это, во-первых, уже не очень профессионально, а во-вторых, скорее всего, эмоциями надиктовано.
Линус об этом и пишет.
Линусу за мнение, конечно, спасибо, но лично я безболезненно использую классы, шаблоны, RAII-конструкции и прочие приятные плюшки. А разбрасываться виртуальными функциями или try-catch блоками в критическом коде никто не заставляет.
А качественно сделать проект выгодно?
Обычно — нет. Jaguar — качественней ford focus, но многие ли на нем ездят? QNX — качественней linux, но много ли на нем систем?

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

Как правило, график качества в зависимости от затрат выглядит так:

  • в начале: увеличили затраты в 2 раза — увеличили качество в 5 раз
  • в середине: увеличили затраты в 2 раза — увеличили качество в 2 раза
  • в конце: увеличили затраты в 2 раза — увеличили качество на 20 процентов


Где-то в середине есть точка оптимума, на которую и надо ориентироваться.

Потому что с кучкой джунов вы далеко не уедете, при чем не только на С.
Само собой. Но это не повод переплачивать за высококачественного супер-сеньора, который бы 98% времени выполнял задачи обычных сеньоров и мидлов.

Вы всегда можете написать процедурно, если вдруг что-то никак не налазит на ООП.
А так и делается. Модульное программирование с элементами ООП.
Точно так же этот человек написал бы кучу процедур/функций для полноценности api работы с глобальными данными.
Вы не правы, объект глобальных данных там тоже был написан. Думаю, что модульная реализация была бы сильно короче. Проблема была в наследовании слишком разных сущностей от общего предка. Ну и идиотской попытке скрестить ужа с ежом.

Думаю, что без ООП у него не было бы мыслей, что совсем разные сущности надо приводить к чему-то общему.
Ну вот на C приведение разных указателей к void часто встречается.
Качество иерархии. Для одной и той же предметной области разные люди придумывают разные иерархии. И если иерархия придумана плохо — мы получаем кучу friendly и ООПшное спагетти. Конечно, нерешаемых проблем нет и программист высокого класса может для любой задачи придумать хорошую иерархию. Но средних программистов больше, чем хороших.
Это ведь проблема любой парадигмы и любой архитектуры ПО в целом.
Почему же? Попробуйте аргументировать.

На мой взгляд, эта проблема в большей степени касается именно ООП. Причин несколько:

  1. В ООП цена переделки иерархии на поздних этапах выше, чем в иных методологиях.
  2. Зачастую задач не иерархична, и ее реализация в ООП представляет собой натягивание совы на глобус. В результате сова просто лопается.
  3. Цена ООП-решения больше зависит от выбора иерархии, чем иных решений.
Простите, но ведь это вы придумываете демагогические «факты», потому и ваша задача их подтверждать.

В ООП цена переделки иерархии на поздних этапах выше, чем в иных методологиях.
Пруф?

реализация в ООП представляет собой натягивание совы на глобус
Пруф?

В результате сова просто лопается
Пруф? Пруф, что лопается только при парадигме ООП?

Цена ООП-решения больше зависит от выбора иерархии, чем иных решений.
Пруф?

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

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

А вот этот трюк — очень типичен для сторонников ООП. Этот очень важный симптом для постановки диагноза «ООП головного мозга». Собственно именно этот симптом и приводит к холиварам при обсуждении ООП.

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

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

Хрестоматийным пример — разная стоимость денег на разных этапах проекта.
Понятно, аргументировать не можете. :-(
А вы это и не пытались делать

А вот этот трюк — очень типичен для сторонников ООП. Этот очень важный симптом для постановки диагноза «ООП головного мозга». Собственно именно этот симптом и приводит к холиварам при обсуждении ООП.

Вам объясняют, что ООП не всегда является лучшим решением, а в ответ вы требуете доказать, что ООП всегда хуже

О! А теперь вы занимаетесь подменой тезиса. Ну уж нет, не получится такая грязная демагогия. Пруф, что я требовал доказать, что ООП всегда хуже? Я лишь сказал, что у каждой идеологии есть свои проблемы, процитируйте в этом топике, где я хоть раз сказал, что ООП — лучшее решение. Вы хейтите ООП, а я лишь пытаюсь указать, что совершенно неаргументированно. Вы привели кучу домыслов и ни разу не подтвердили фактами. Или вы считаете, что цитата на Баш — это доказательство? Понятен уровень вашей аргументации. Ну так на Баше и высказывайтесь, ваша цитата может даже в лучшее бездны попадет, чего тогда на Хабре выступаете? Я в этом топике ни разу не топил за ООП я лишь указывал на то, что ваш коммент — всего лишь неаргументированное хейтерство.

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

И еще пытаетесь сменить направление, приписав мне то, что я в этом топике ни разу не говорил.

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

Так вот, вы там привели кучу спорных заявлений, подтверждать будем или просто сольемся и выльем очередную порцию лжи и демагогии?
Пруф, что я требовал доказать, что ООП всегда хуже?
Ноль проблем:
И пруф, что зависимость от выбора иерархии повышает сложность поддержки, в сравнении с ЛЮБОЙ другой зависимостью при создании архитектуры на любой парадигме.

Я лишь сказал, что у каждой идеологии есть свои проблемы,
С этим согласен. Только рассматривать надо не кубическую лошадь в вакууме, а триаду — методология + люди + задача. И минимизируем мы не сложность поддержки, а общую стоимость на конкретный этап работы.

Или вы считаете, что цитата на Баш — это доказательство?
А это хрестоматийный пример, когда на разных этапах стоимость денег разная. В любом случае, конкретные примеры из практики плохи тем, что я знаю о них больше вас (видел код, понимаю предметную область и так далее). Поэтому пример, который мы знаем одинаково — лучше.

Вот, оказывается, куда-то ООП и идеально подходит.
Конечно, есть. Вы перечитайте исходный пост, там об этом прямо написано. И про предметные области с естественной иерархией, и про предметные области с повторным использованием и про качество программистов.

Вы однозначно, без «но» заявили, что «сова просто лопается».
Понятно, у вас другой диалект русского языка. Украина? Перечитайте пост и обратите внимание на слово "зачастую". Это и есть то самое «но», которое вы не увидели.

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

Ноль проблем:
И пруф, что зависимость от выбора иерархии повышает сложность поддержки, в сравнении с ЛЮБОЙ другой зависимостью при создании архитектуры на любой парадигме.


Вы сказали, что цена для ООП выше чем для всех иных решений, то есть, в данном контексте оно хуже всех:
Цена ООП-решения больше зависит от выбора иерархии, чем иных решений.


Вот я и попросил доказать ваши слова. Нет, вы не сказали, что «ООП не всегда является лучшим решением» (я, кстати, угадал, вы решили пойти по пути лжи и демагогии). Вы сказали однозначно, процитирукю второй раз:

Цена ООП-решения больше зависит от выбора иерархии, чем иных решений.


И в этой цитате нету никаких «но» и «зачастую». Вы заявили, что цена больше зависит от выбора иерархии, чем в иных решениях. Вот и докажите.

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

Поэтому пример, который мы знаем одинаково — лучше.

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

что я знаю о них больше вас

Вы меня не знаете. Перестаньте раз за разом атаковать мою личность, подтверждая слабость своей позиции.

Перечитайте пост и обратите внимание на слово «зачастую». Это и есть то самое «но», которое вы не увидели.

Вот только оно есть только в одном из трех пунктов и в том, где вы утверждаете, как вы выразились, что «ООП хуже всех» — его нету. Да и «зачастую» — тоже означает, что чаще, чем обычно, что тоже требует пруфов.

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

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

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

Не вижу смысла спорить с собеседником, который плохо понимает смысл слов.

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

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

Мой вам совет — найдите человека с нативным русским языком и вместе с ним перечитайте, что я писал, а что не писал. А уж потом — возражайте
Ибо цена переделки, не затрагивающей иерархию, в ООП ниже, чем в других методологиях. Поэтому вы просто не сумели понять написанный мной текст.
Все я прекрасно понял. Вы утверждаете, что переделывать иерархию — дороже, чем где-либо еще. Повторюсь: нужен пруф. Пока это лишь слова непоследовательного хейтера.
Как пример — много ли вы знаете поддерживаемых игр 20летней давности?
Добавлю ещё на счет этого. Игры Valve, idSoftware и на базе Unreal Engine поддерживаются уже от двух десятилетий, постоянно развивая один и тот же движок. Основная причина постоянных усовершенствований движка — безумно быстрая и жёсткая смена технологий и требований, которая многим другим направлениям даже не снилась. 3d max, Blender, Photoshop, Cubase развиваются уже более двух, а то и трех десятков лет. Банальной Lineage 2 уже 15 лет, а первой — уже 20. World Of Tanks выпускает живые обновления для огромной базы пользователей до 6 раз в год уже 8 лет. Far Cry вышел в 2004, а начали разработку ещё раньше — уже больше 15 лет. Про CryEngine слыхали? Первая версия Fifa вышла 25 лет назад и новая игра выходит каждый год.
Угу, меньше процента. Основная масса — выпущена и заброшена. Если говорить про id Software, то где сейчас Spear of Destiny? Когда последнее обновление вышло? :-) Движок — да, развивается. А вот сама игра — нет. И таких, заброшенных игр — 99%, если не 99.9.
Как и любого софта на любой парадигме. Сколько вы знаете программ на Хаскеле, которые поддерживаются более 20 лет? Я назвал, ваша очередь.

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

Кстати, Копье Судьбы, которое вы почему то привели в пример — Всего лишь одна из частей Вольфенштейна, недавно вышло его новая часть, Так что серия живет, Спасибо, что поинтересовались
ОТЛИЧНО! Вы начинаете понимать главное.

Вопрос — если заведомо известно, что через год после выпуска продукт забросится, стоит ли тратить деньги на упрощение 20 летней поддержки?

Нужна ли нам технология, «позволяющая развивать десятилетиями», если мы заведомо не собираемся это делать? А если не нужна — то это оверхед. Ибо ленчей даром не бывает и за упрощение поддержки надо платить. Больше или меньше — зависит от задачи.
Оппа! Переобулись! Сначала вы топили, что чем дольше поддержка, тем ООП — хуже. Цитирую:
В ООП цена переделки иерархии на поздних этапах выше, чем в иных методологиях.

А теперь оказывается, что ООП надо применять, если поддержка — долгая и именно тогда он нужен.

У вас какая-то каша в голове. Хейтерство не идет на пользу вашим аргументам. И определитесь с позицией. Вы переобуваетесь быстрее, чем я успеваю читать ваши сообщения.
И в третий раз — учите русский язык. Мне действительно лень писать на уровне начальной школы и иностранцев. Найдите человека с нативным русским — и он вам все растолкует.

Ну серьезно — ну лень мне! :-) У меня своей работы хватает. Ну не учитель я русского для иностранцев.

Ну ладно, одну фразу объясню на уровне детского сада
В ООП цена переделки иерархии на поздних этапах выше, чем в иных методологиях.

Начинаем разбирать текст.

Выше цена переделки чего? Иерархии. Цена других переделок — обычно ниже.

Цена переделки иерархии выше когда? На поздних этапах.

Слово «этапы» во множественном числе. А вся поддержка — это один этап. Значит речь не только о ней. А намного больше — про поздние этапы написания кода.

Речь о простой ситуации. Придумали иерархию Написали классы, две трети проекта — и поняли, что оставшуюся треть с этой иерархией написать будет очень тяжело.

Вся фишка ровно в том, что цена архитектурной ошибки в других методологиях ниже. И менять архитектуру проще. И придумывать её можно на более поздних этапах.


Знаете что? Попробуйте придумать иерархию классов для книг. В комплексе — издательство + типография + магазин + библиотека. А я буду подбрасывать вам объекты, которые не совсем книги. Но в отдельных местах встречаются. Ну и посмотрим, легко ли будет их вам вместить в иерархию.

Вот так вот, на собственном опыте — вы может быть поймете, что цена передки иерархии очень высока. И что кривая иерархия — губит весь проект.

Но это — если вам понимания русского языка хватит. Спорить о смысле фраз — мне откровенно лень.

P.S. Почти 30 лет назад я участвовал в написании библиотечной системы. Так что предметную область помню.
Знаете что? Попробуйте придумать иерархию классов для книг. В комплексе — издательство + типография + магазин + библиотека. А я буду подбрасывать вам объекты, которые не совсем книги. Но в отдельных местах встречаются. Ну и посмотрим, легко ли будет их вам вместить в иерархию.
Я могу придумать плохую нерасширяемую архитектуру на любой парадигме. Знаете, что это доказывает? Ничего.

Попробуйте и придумайте хорошую архитектуру для данного примера. Сумеете?

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

Как пример — Торвальдс решил, что без ООП лучше. И, похоже, не прогадал.
Попробуйте и придумайте хорошую архитектуру для данного примера. Сумеете?
А Вы задачу нормально сформулируйте. Вы ведь так и не сказали, какие операции можно совершать над какими типами. Иерархия не строится для «типографии», она строится для функционала «типографии».
Как пример — Торвальдс решил, что без ООП лучше. И, похоже, не прогадал.
Даже в этом примере, который Вы привели выше, Торвальдс черным по белому пишет, что использует ООП, просто ему для этого не нужны плюсы, хватает чистого С. Похоже, у Вас все меньше союзников :)
А в том и гадость, что полностью задачи такого объема не ставятся. Есть начальное ТЗ, а через пару лет — захотели сделать что-то ещё.

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

Ибо для любой методологии найдется место в коде, где именно её элементы наиболее уместны. Даже для goto есть парочка случаев, когда именно его стоит применять.
Торвальдс сказал что ему не нужен плюсовый булшит со всей его стандартной библиотекой и текучими абстракциями. Нигде выпадов в сторону ООП я не встречал.
Попробуйте придумать иерархию классов для книг. В комплексе — издательство + типография + магазин + библиотека.

Да-да, вы что-то такое рассказывали в другой статье. Кажется, вы считаете, что ООП = наследование, и пихаете его где надо и где не надо. В частности, вы считаете, что книга непременно должна от чего-то наследоваться. Неудивительно, что у вас при изменениях возникают проблемы с иерархией.

У книги, брошюры, журнала — много общего. С другой стороны — романы, сборники, антологии, энциклопедии — имеют свои отличия и рамках единого типа «книга» им неуютно.

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

С другой стороны, в предыдущей дискуссии обсуждался неклассический путь:

  • Наследование по данным только от единого базового класса
  • Наследование интерфейсов (абстрактных классов) по методам.


Давайте не будем спорить, ООП это или нет, но такой вариант вполне устойчив к архитектурным проблемам.

А если ещё добавить указатели на конкретный метод конкретного экземпляра (как в дельфи), то архитектурные проблемы просто обходятся малой кровью.

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

Вот в этом у вас и проблема. Вы придумали какое-то "полноценное ООП", и критикуете именно его, в то время как все остальные под ООП понимают нечто другое...

НЛО прилетело и опубликовало эту надпись здесь
У книги, брошюры, журнала — много общего. С другой стороны — романы, сборники, антологии, энциклопедии — имеют свои отличия и рамках единого типа «книга» им неуютно.

Если вам заведомо известно, что у них есть отличия, которые создадут проблемы при наследовании, зачем наследовать их от книги и тем более от абстрактного товара?


Если выкинуть наследование, то выкидываются виртуальные методы (полиморфизм подтипов). И это будет не ООП.

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

В комплексе у нас будут 4 иерархии. Нет никакой нужды в системе управления книжным холдингом моделировать книги в разных контекстах одним объектом :)
Почему нет?
Как минимум — есть выплата авторских. Кроме потиражных, она может быть от числа проданных экземпляров. И даже от числа выданных книг в библиотеке. Как ни странно, ещё во времена СССР устроили учет песен, исполненных в ресторанах. Ровно для цели выплаты авторских

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

И бухгалтерам это давно известно, что книга как объект авторского права и как материальный объект — это разные объекты учёта, зачастую в бухгалтерском учёте вообще связей не требующей.
Знаете что? Попробуйте придумать иерархию классов для книг. В комплексе — издательство + типография + магазин + библиотека. А я буду подбрасывать вам объекты, которые не совсем книги.

А где вы тут иерархию книг увидели? Всё, что вы перечислили — отдельные классы ссылающиеся друг на друга.

Вам объясняют, что ООП не всегда является лучшим решением, а в ответ вы требуете доказать, что ООП всегда хуже.

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


Если это не так, то уточните, что вы имели ввиду под своими п.1, п.2., п.3.
Например:


В ООП цена переделки иерархии на поздних этапах выше, чем в иных методологиях.

Это надо читать как:


В ООП иногда цена переделки иерархии на поздних этапах выше, чем в иных методологиях.

?
С-но, иногда она такая же, а иногда — ниже? Так?

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

А какой случай типичный — это зависит от области.

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

А почему в ООП также нельзя?

НЛО прилетело и опубликовало эту надпись здесь
Во-первых, где ООП смешивает данные и функции? Во-вторых, утверждение, что это плохо<...>

В конце 90х, когда я изучал программирование нас учили не так, как сейчас:
из хаоса первичного бульона
Из лапши несвязанного кода возникло процедурное программирование. Процедура принимает параметры (данные) и, возможно, изменяет их. Функция — это процедура, которая возвращает значение. В общем виде, данные и процедуры не связаны друг с другом и всегда, ВСЕГДА процедура должна проверять входные параметры на корректность — потому что не знает кто ее вызовет и что подсунет. Затем придумали модульную организацию кода. Модуль может иметь приватные процедуры, на вход которым подаются проверенные данные. Pascal — типичный представитель, вы учили его три года. Код собран в процедуры, а процедуры — в модули. Но в модульном программировании есть проблема. Публичная процедура может иметь много параметров, и не все комбинации имеют смысл — поэтому для взаимосвязанных аргументов количество проверок экспоненциально растет.
ООП — эволюционно лучше в написании ПО. Здесь данные и операции над ними увязаны в специальную сущность — объект. Процедура в ООП называется метод. Объект имеет внутреннее состояние — свои свойства — и это ключевое отличие от модуля. Вы вызываете метод, а данные он берет из свойств и нескольких аргументов процедметода. Если спроектировать и написать объект правильно, при вызове метода достаточно проверить только пару аргументов, так как все взаимосвязанные свойства гарантированно будут согласованы. Это здорово разгружает от написания проверок всех-всех-всех аргументов. Правильно написанный объект не может иметь несогласованного внутреннего состояния.
Поэтому логичный вывод — да, ООП объединяет состояние и методы работы с состоянием. И это — его преимущество перед более ранними парадигмами. Только потом нам рассказывали как ООП использует полиморфизм, инкапсуляцию и наследование — эти термины, кстати, мы изучили на этапе модульного программирования, еще до знакомства с ООП.
После знакомства с ООП и первых программ на нем на разборе мы услышали: Вообще-то, ты написал модуль, а не объект. Но не расстраивайся, многие пишут в процедурном стиле на ООП языке. Глядя на некоторые пакеты, состоящие из DTO и Helpers, я понимаю что они недалеко ушли от модулей. Потом нас обнадежили: Я за это буду ругать не очень сильно. Потому что ВЕСЬ код в ООП стиле написать тяжело. Гораздо позже я понял, что и не нужно.
Понял, что я не хочу воевать за чистоту языков и парадигм — человеческих или программирования. Я стараюсь говорить и писать чисто, использую альтернативы где удобно и не стремлюсь к абсолютной чистоте. Я понимаю: протекание посторонних конструкций что в ООП, что в русский неизбежно — ведь они развиваются.
Всё это конечно хорошо. Но когда вам понадобиться написать масштабный проект (мегабайт на 50-100 кода), который потом надо сопровождать и развивать годами, то… или ООП или никак.

Главное преимущество ООП — управление сложностью.
Соглашусь. Я читал про ООП еще в школе, когда на паскале игры писал. Я понимал, что это круто, но у меня очень долго не получилось применять его с пользой, даже после того как я начал получать за программирование деньги. Я пытался притянуть его за уши, но у меня всегда получались просто классы-склады функций. Все потому что писал я модули для ЦМС, где работать приходилось максимум с парой таблиц и парой форм. Впервые ощутил ООП я только когда написал свой первый микросервис, который работал уже со всей базой и имел десятки разных методов в АПИ.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
ООП это не парадигма, это метода.
Объе́ктно-ориенти́рованное программи́рование (ООП) — методология программирования
wiki
НЛО прилетело и опубликовало эту надпись здесь
Странная подборка аргументов. Откуда она?
Всё, что есть в ООП, уже давно есть в других парадигмах

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

ООП не запрещает создавать объекты без состояния, ровно как и объекты без поведения.
Наследование закрепощает программу, делает трудным внесение изменений

Как и любой другой инструмент, может быть губительным не в тех руках. Возможно, речь о том, что наследование — очень сильная связь от общего к частному, а с сильными связями стоит быть осторожным. Уж точно наследование существует не для «выделения общих методов в базовый класс». Это как с KISS, который многие ошибочно считают призывом выносить все похожие участки кода в отдельный метод.
Инкапсуляция не имеет смысла

Нет, у данных аргументов правда существует какой-то источник?
Если не предполагается расширять родительский класс третьим классом — такое наследование попросту бессмысленно. Если вы создаёте магазин спиртных напитков, то классы Beer, Vodka и Vine можно унаследовать от класса Alcohol, но совершенно не нужно создавать ещё и класс Drinks, если только вы не хотите продавать ещё и, скажем, парагвайский чай.

Кроме классов существуют еще и интерфейсы, которые с точки зрения SOLID как раз желательно сегрегировать.
Но ведь никто не мешает создать, например, иерархию, где все реки мира (Конго, Сена, Темза, Амазонка, Колыма и т.д.) являются объектами одной всеобъемлющей «Реки», которой присущи свойства (например, состоит из воды) и действия (например, течёт), а уже она будет наследоваться от «Водоёма», который тоже состоит из воды, а от «Водоёма» можно унаследовать ещё и «Озеро», объектами которого будут отдельные озёра (Байкал, Каспийское море, Титикака и т.д.).

Например: человек — объект из реального мира. Он может ходить, бегать, кушать, срать спать, играть в футбол, смотреть футбол, но, к сожалению, я тут не могу всё перечислить, да и, честно сказать, всё перечислять было бы противно.

Это, вроде как, про одно и то же. Кстати, пример с человеком очень хорошо показывает, что наследование не может заменить композицию (иногда и наоборот). Как вы имплементируете человека, который играет в футбол от человека, который плавает, сделаете его наследником человека? А что нужно сделать, чтобы получить человека футболиста-пловца? Обычно, в таком случае композиция выиигрывает. Вообще, Composition over inheritance — один из основных принципов ООП, описанный еще в небезизвестной Design Patterns от GoF.
Но даже миллионы мух не убедят нас, что навоз — это вкусно

В целом статья получилась сумбурная, как это бывает обычно, когда она пишется «чтобы было». Цель Автора, вроде как, понятна, но для этого было достаточно прочитать последний абзац без всей воды с сомнительными аргументами и нечеткими контраргументами.

Публикации