>>49841Но ECS к ООП абсолютно перпендикулярен. То есть, вообще никакого отношения, кроме того, что его можно реализовать с помощью объектов. В реальности у тебя тут будет HitManager.do_attack( player, monster );, что есть антипаттерн.
> Все решается
Начну немного издалека, но ты потерпи.
Ещё относительно недавно на вопрос "Что такое ООП?" следовал ответ "Полиморфизм, инкапсуляция, наследование". Сейчас люди стали чуть менее категоричными, но они продолжают-таки признавать, что эти принципы есть основные киты ООП. При этом полиморфизм в ООП - сабтайпинг, о котором будет следующий абзац - это подмножество полиморфизма вообще, и полиморфизм есть, например, в StandardML, а вот ООП туда не завезли. Да что там, его можно сделать даже в сишке; тебе процесс не понравится, но можно. Инкапсуляция тоже есть как в SML, так и в сишке. Даже некоторые ассемблеры (тот же масм, который использует сишный линкер) позволяют тебе делать локальные для файла функции/переменные. Остаётся наследование. Вот это уже чистая ООПешная тема. Что забавно, ибо в последнее время программисты объектной ориентации от него открещиваются. На этом можно было бы и закончить, но я продолжу.
Теперь рассмотрим Liskov Substitution Principle. Это та самая L из SOLID. Ещё поговаривают, что это l из Agile и даже из Waterfall, где их сразу две. Этот принцип настолько важен, что в честь него назвали язык LISP. Я буду говорить про его ООП интерпретацию. Звучит он так: если у тебя есть базовый класс А, и есть наследующийся от А класс В, то везде, где ожидается объект класса А, можно передать объект класса Б, и семантика программы не изменится. Если подумать, то оно логично. Это, как говорят у них, цельная точка (whole point) наследования. В конце концов, если у тебя есть ящик, который может хранить фрукты, но не бананы и яблоки, а некие абстрактные фрукты, то такой ящик тебе будет не очень полезен.
Теперь, держа в голове последние два абзаца, рассмотрим пример из почти любой книжки про введение ООП в начинающих. Этот пример, например, можно увидеть в книге Страуструпа Programming: Principles and Practices in C++ (или как-то так) где-то, если я не ошибаюсь, в первых трёх-четырёх главах. Мы делаем графическую объектно-ориентированную библиотеку. У нас есть базовый класс Shape, у нас есть много-много его наследников, и мы с ним можем делать разные вещи - рисовать на экран, печатать, считать площади и т.д. и т.п.. Мы доходим до реализации классов Rectangle и Square, и у нас появляется три пути, ради рассмотрения которых всё это и писалось.
Итак, путь первый, он же не_ооп: class Rectangle extends Shape и сlass Square extends Shape
Тут всё очевидно. Никакого переиспользования кода, отношения "is a" не выражены и т.д. Любой оопрограммист скажет тебе, что это неправильно и никто так не делает, а если делает, то он дурак и должен получать клавиатурой по голове пока не осознает. Ну, или хоть пусть какого-нибудь Буча почитает. Поэтому...
...Путь второй, он же очевидный и неправильный: class Rectangle extends Shape и class Square extends Rectangle
И вроде бы тут не к чему придраться, но я не просто так же написал абзац про LSP выше. Рассмотрим код
```function scale( r:Rectangle ) {
r.width *= 10;
r.height *= 2;
}
scale( new Square( 4 ) );
```Этот код скомпилится, но он нарушает LSP. Более того, тут почти undefined behaviour, потому что этот код зависит от того, как реализован сам язык, как реализована библиотека шейпов, и может быть даже как компилятору позволено код оптимизировать. Как же правильно? Если помедитировать над LSP, то можно прийти к выводу, что дочерние классы могут только расширять родительские, но ни в коем случае не ограничивать. Ведь именно из-за введения нового инварианта в Square у нас и получилась фигня. Поэтому правильный код...
...Номер три: class Square extends Shape и class Rectangle extends Square.
И всё, вроде бы хорошо, ну, кроме "очевидности ооп", о которой тебе говорят в книжках. Знаешь, вот эти все истории про "собачка - это животное, и кошечка - это животное, поэтому мы делаем базовый класс, наследуемся от него, и кошечка говорит "мяу", а собачка говорит "гав" - смотрите, как просто мы можем моделировать реальный мир. И ты читаешь эти истории, веришь им, а в реальности у тебя вот такая фигня выходит. Но если бы на этом всё закончилось, то я бы тут не распинался. Consider, как говорится, зе following
```if ( new Rectangle( 15, 45 ) is Square ) throw "somethin' really fucked up's goin' on here m8";```
Что, по-твоему, сделает этот код? Я, конечно, не математик, но какое-то внутреннее чувство мне подсказывает, что прямоугольник со сторонами 15 и 45 - это не квадрат.
Вот эта вот задача не решаема в ооп. Фундаментальная проблема всея компьютер саенса
разводит руками. В книжках об этом умалчивают, потому что иначе никто дальше первой главы (с этим примером) читать не будет.