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

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

Читанул по-диагонали — вроде всё очень даже гут. Спасибо за материал, солилная работа! Буду использовать как референс для интересующихся людей )
Вы описали не инкапсуляцию, а сокрытие.
Вы описали не инкапсуляцию, а сокрытие.

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

Единственное что, возможно выскажу непопулярное мнение, но:

  • Интерфейсы — это такой костыль для языков, разработчики которых не хотели реализовывать множественное наследование, а точнее бороться с «ромбами» реализаций. Классическая иллюстрация — Java, где после появления default реализаций, интерфейсы это по сути классы без non-static field'ов. То есть абстракция получилась очень дырявая.
  • Паттерны проектирования, в общем случае — зло. Либо вынужденное зло — когда используются как костыль, чтобы скрыть недостатки используемых языков / платформ (например паттерн Model-View), либо, что еще хуже — лень, когда разработчик не пытается использовать медленное мышление — структурировать задачу и придумать правильное решение, а включает быстрое мышление и начинает мыслить шаблонами, городя «фабрики фабрик» со всеми вытекающими.

Ну и к инкапсуляции, как к коктейлю, который одновременно отвечает за обеспечение модульности / пространств имен, реализацию полиморфизма (причем одиночного), синтаксический сахар в виде автоподстановки первого параметра (this), ну и функционал структур (то есть реализацию работы с первичными данными — field'ами) тоже есть много вопросов. В частности, например, она усугубляет объектно-реляционный семантический разрыв. Но это отдельная большая тема.
НЛО прилетело и опубликовало эту надпись здесь
Зло — это когда машинный код после компиляции все еще имеет в себе куски типизации и тратит время и стек на проверку типов в рантайме.

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

Ну и не понятно какое отношение это имеет к «дырявости» интерфейсов (в смысле их дублированию логики классов).

Паттерны проектирования — это процесс стандартизации индустрии. Как минимум чтобы люди друг друга понимали. Как максимум — чтобы была возможность переиспользовать код. Зло — это когда почти каждая программа имеет свой текстовый редактор и спеллчекер, когда нам нужна 20Гб операционка чтобы отредактировать *.txt файл.

Для того, чтобы люди друг друга понимали, нужно проектировать и писать понятный код. Какое отношение паттерны проектирования имеют к повторному использованию кода (в частности к переписыванию текстовых редакторов) тоже неясно. То есть существует пару паттернов именно для этой задачи, но это именно что костыли, когда либо используемый модуль, либо использующий модуль спроектированы не очень качественно и их нужно «склеить».
Если честно я тоже не совсем понимаю что вы так прямо чуть ли не ненавидите паттерны. Возьмём те же IOC или Dependency Injection: вы считаете что они не нужны, что они «зло», «костыль», «неправильная проектировка архитектуры» или «нежеленаие разработчиков найти правильное решение»?
Если честно я тоже не совсем понимаю что вы так прямо чуть ли не ненавидите паттерны. Возьмём те же IOC или Dependency Injection: вы считаете что они не нужны, что они «зло», «костыль», «неправильная проектировка архитектуры» или «нежеленаие разработчиков найти правильное решение»?


А причем тут IOC и DI. Они не создают дырявые абстракции (а точнее классы/объекты), и достаточно спрятаны от разработчика (в коде всего одна аннотация).

Я про всякие фабрики, хранители, посредники и другие суррогаты по ссылке в статье.
А причем тут IOC и DI.

При том что они на мой взгляд вполне себе являются «паттернами проектирования», а вы выше написали:
Паттерны проектирования, в общем случае — зло


И даже если не относить IoC и DI к паттернам, с чем многие вполне себе могут поспорить, то IoC и DI тоже можно использовать не к месту и/или криво и тем самым насоздавать себе кучу проблем. Они в этом плане мало чем отличаются от фабрик, декораторов, фасад и прочего.

Практически любой паттерн имеет свою область применения и вредит если используется не к месту. Но это не значит что паттерны зло «в общем случае»…

При том что они на мой взгляд вполне себе являются «паттернами проектирования»

Я специально зашел по ссылке автора и там ни IoC ни DI нет. А я под паттернами проектирования подразумеваю достаточно конкретную вещь (помню даже книжку по ним лет 15 назад читал) и именно то, что описано по ссылке автора.

И, еще раз, эти концепции (IoC и DI) не создают дополнительные публичные классы и объекты. Это принципиальный момент, поэтому они практически не вносят дополнительной сложности.
Я специально зашел по ссылке автора и там ни IoC ни DI нет.

Если я не ошибаюсь, то внизу там была ссылка на DI, да и так нелюбимая вами «фабрика» это вроде бы один из вариантов IoC.
Кроме того как минимум в английской версии статьи они присутствуют оба: en.wikipedia.org/wiki/Software_design_pattern

А я под паттернами проектирования подразумеваю достаточно конкретную вещь (помню даже книжку по ним лет 15 назад читал) и именно то, что описано по ссылке автора.

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

И, еще раз, эти концепции (IoC и DI) не создают дополнительные публичные классы и объекты.


Во первых они создают оверхед, как минимум в виде конфигурации и мэппинга. Во вторых они часто создают проблемы с зависимостями от библиотек или их отсуствием. И в третьих я конечно не знаю что вы сейчас конкретно подразумеваете под «дополнительные публичные классы и объекты», но практически все DI варианты на C#, которые я видел, имеют какой-нибудь DependencyLocator и/или коллекцию мэппингов.
Вы извините но языки программирования развиваются и появляются новые языки и фреймворки. И это приводит к появлению новых паттернов. И заявлять, что что-то не является паттерном программирования потому что вы не прочитали об этом в какой-то книжке 15 лет назад, это на мой взгляд немного странно.

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

Давайте так, чтобы уточнить, когда я говорил про IoC и DI я имел ввиду, что они встроены в язык / платформу (вроде как в Java Spring / .Net). И все создаваемые при этом классы разработчику не видны.

А когда IoC и DI делают явно в коде (создавая proxy-классы и заменяя на них field'ы и конструкторы) это ад. Такой код читать / рефакторить очень тяжело.
Просто если абстрагировать паттерны проектирования до всех «техник применяемых в программировании», то они автоматически потеряют всякий смысл, так как будут значить все что угодно.

Угу. Вот только почему-то большинство известных мне программистов считаю DI и IoC паттернами. Да и гугл с вики с этим вроде бы согласны :)

Давайте так, чтобы уточнить, когда я говорил про IoC и DI я имел ввиду, что они встроены в язык / платформу (вроде как в Java Spring / .Net). И все создаваемые при этом классы разработчику не видны.

Более менее «встроены» они начиная с .Net Core и даже там они невидимыми не являются, так как минимум конфигурировать всё это дело кому-то надо.

А когда IoC и DI делают явно в коде (создавая proxy-классы и заменяя на них field'ы и конструкторы) это ад. Такой код читать / рефакторить очень тяжело

Угу. И именно так всё это делалось пока не появились удобные фреймворки. И даже такой вариант на мой взгляд раньше часто являлся лучшим решением чем не иметь никакого IoC :)
Угу. И именно так всё это делалось пока не появились удобные фреймворки. И даже такой вариант на мой взгляд раньше часто являлся лучшим решением чем не иметь никакого IoC :)

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

После фреймворков, они стали частью языка / платформы и перестали быть паттернами, в моем понимании этого слова.

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

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

И почему вы считаете, что они суррогаты?

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

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

Интерфейсы — это такой костыль для языков, разработчики которых не хотели реализовывать множественное наследование, а точнее бороться с «ромбами» реализаций. Классическая иллюстрация — Java, где после появления default реализаций, интерфейсы это по сути классы без non-static field'ов. То есть абстракция получилась очень дырявая.

С другой стороны, проблема "ромба" именно при наличии полей в базовом классе и проявляется наиболее сильно!

Интерфейсы — это такой костыль для языков

Про интерфейсы хотелось бы услышать мнение господина lair

Что конкретно?

Что конкретно?

Что думаете по поводу этой мысли?

Думаю, что там сильно больше одной мысли. Но если по верхам:


Интерфейсы — это такой костыль для языков, разработчики которых не хотели реализовывать множественное наследование

Мне не нравится слово "костыль", но по сути это недалеко от того, что я думаю: для потребителя (то есть того, кто откуда-то получает Something some, а потом делает ему some.Do()) различие между интерфейсом и классом сугубо техническое, и без него прекрасно можно обойтись. При этом разделение на реализацию и абстракцию (или, точнее, внешний контракт) — очень важно; не важно, чем этот контракт выражается, важно, чтобы были способы его знать и, по возможности, навязывать.


Паттерны проектирования, в общем случае — зло.

… а по этому поводу уже все сказали: в общем случае, паттерны проектирования — это совсем не зло, потому что это типовые решения типовых задач. Общий словарь.

Паттерны проектирования, в общем случае — зло.
Паттерны проектирования — это просто типовые решения типовых задач, только и всего (а чтобы не путаться, вместо слова «паттерн» употребляйте более привычное русскому уху слово «шаблон»). Зло появляется, когда в них пытаются увидеть что-то большее, от этого происходят всякие бессмысленные холивары, как у вас с Kanut.
более привычное русскому уху слово «шаблон»

Перевод слов «паттерн» и «шаблон» — образец.
Есть такое понятие — «стандартный образец».

Почему бы для рисования сразу не использовать UML?

Почему бы для рисования сразу не использовать UML?

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

Слабое объяснение. Крайне субъективное и слабое. Но мало того: UML — есть стандарт, а ваша методика рисования — есть только ваша методика рисования. Вот возьмёт новичок и по вашим диаграммам, пойдёт в профессию и столкнётся с профессионалом, который не долго думаючи спросит: "а почему ты, новичок, рисуешь диаграммы как-то странно? Ты что — дурак? Ведь есть же UML — зачем ты переизобрёл велосипед в 115 раз?"
Как будет чувствовать себя новичок?

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


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

К любому "рисованию диаграмм по своему" шли через БАЗУ рисования диаграм вообще. А база в IT для этого — UML диаграммы.


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


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

А база в IT для этого — UML диаграммы.

… а на чем основано это утверждение?


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

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


Особенно если учесть, что UML не имеет строгой нотации

Вот именно поэтому я и не вижу смысла за него ратовать, если споров за то, как в нем что-то правильно изобразить, будет больше, чем споров об изображенном (real life story).

… а на чем основано это утверждение?

Первая буква U из UML — отвечает на этот вопрос.


без отсутствия общего языка. Но вы почему-то считаете, что UML таким языком является

А чем вам UML не общий? И без шпаргалки его сможет прочесть любой, сноски нужны — для понимания нюансов. Но вот в чём штука — чтобы понять ЛЮБУЮ предлагаемую ВАМИ (или автором) диаграмму — сноски обязательны. Но в вашем случае — они нужны априори, а в UML без них, за счёт хоть какой-то известности — можно обойтись. Так что он уже более общий, нежели ваш (или автора) продукт.


как в нем что-то правильно изобразить

Кто определит понятие "правильность изображения" — тот собственно и решит все споры.

Первая буква U из UML — отвечает на этот вопрос.

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


А чем вам UML не общий?

Тем, что не все его знают.


И без шпаргалки его сможет прочесть любой

Неа. И, что важнее, все прочитают по-разному.


Но в вашем случае — они нужны априори, а в UML без них, за счёт хоть какой-то известности — можно обойтись.

Вот это как раз и иллюзия. Может быть можно. На практике я такого не видел.

как кто-то решил назвать свой язык.

Не кто-то, а люди, которые в далёком 1995 году уже столкнулись с проблемой рисованяи классов и постарались решить эту проблему, пройдя несколько процессов улучшения и попутно внедрив систему в общеобразовательный стандарт ВУЗов не только в США, но и по всему миру.


Неа. И, что важнее, все прочитают по-разному.

ну не знаю не знаю, но класс — я различу. Интерфейс тоже… С ассоциациями — морока конечно, но тоже можно разобраться.
А с другой стороны — я не знаю как в России, но я кроме двух ВУЗов в Латвии знаю как минимум один в Германии — в которых в IT на программиста преподают UML. И представляют как стандарт. Я знаю, что используется в некоторых ВУЗах США.
Это как минимум в несколько раз больше, нежели — изобрести велосипед заново в данной статье.


На практике я такого не видел.

Субъективное мнение. Я видел, как люди — не пугались UMLа :).

Не кто-то, а люди, которые в далёком 1995 году уже столкнулись с проблемой рисованяи классов и постарались решить эту проблему

Никто не спорит с тем, что постарались. Но решили ли?


ну не знаю не знаю, но класс — я различу. Интерфейс тоже…

Вы — различите. Но вы — это не все.


А с другой стороны — я не знаю как в России, но я кроме двух ВУЗов в Латвии знаю как минимум один в Германии — в которых в IT на программиста преподают UML. И представляют как стандарт.

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


Это как минимум в несколько раз больше, нежели — изобрести велосипед заново в данной статье.

Мне кажется, вы не понимаете целей "велосипеда" в данной статье. У него ровно одна задача: пояснять рассуждения в данной статье. Смысла сравнивать его с преподаванием UML ровно ноль.


Субъективное мнение.

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

У нас в ВУЗе UML преподавали и именно как общий стандарт.


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

Любой "диалект" нуждается в "базе", на которую он ложится. И в данном случае: UML - устоявшаяся база, повторюсь.

изобрести велосипед заново в данной статье

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

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

Никто и не говорил, что это объективное обоснование. Там так и написано — это мое субъективное мнение.

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

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

Если бы у меня была цель вместо букваря детям дать чертежи, я бы назвал статью «ООП в UML-диаграммах», а не в картинках. И тогда да, дети после первого класса могут сразу идти на собеседование, владея чертежами.
Чтиво не осилил, плюсую за картинки).
Уфф… отлего. Значит я не один такой. Всем спасибо, очень было интересно, но чтобы этот текст по-настоящему вкурить, надо большую часть изложенного знать достаточно хорошо. )
С теорией применения паттернов все вроде-бы понятно. Теперь хочется научиться правильно декомпозировать задачи (посоветуйте материалов) и правильно применять паттерны, особенно в рефакторинге легаси-кода.
Хочется примеров «best practice», признаков того, что все сделано правильно.
Если бы так просто было определить сделано всё правильно или неправильно, то мир программированиябыл бы райским садом. К сожалению то, что что-то сделано неправильно, обычно выясняется уже после того как оно сделано. Причём обычно гораздо позже.

И какого-то «best practice» на все случаи жизни лично я не знаю. Я даже не могу назвать «best practice» для выбора «best practice для какой-то конкретной проблемы» :)
Как раз это я пытался донести в разделе «заключение».
Не все задачи легко декомпозируются в ООП. Попробуйте для разминки декомпозировать геометрию.
и правильно применять паттерны, особенно в рефакторинге легаси-кода.

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

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

ООП это, в контексте вашего вопроса, просто «инструмент», который по идее просто облегчает написание и поддержку кода.

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

Действительно, зачем?

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

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

class Starship
  {
  private $position;

  public function fly(Vector $coords) {}
  public function moveTo($coords) {}
  public function hyperjumpTo($coords) {}
  public function shootAt($coords) {}
  }


$myStarship=new Starship;
$myStarship->flyTo($someRandomPoint);


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

abstract class Starhip {} 

class StarDestroyer extends Starship {}


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

class SuperStarDestroyer extends Starship {}


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

//Название не совсем удачное, поскольку Imerial/Rebel на самом деле не указывает на владельца
abstract class ImperialStarship extends Starship {}
abstract class RebelStarship extends Starship {}

class StarDestroyer extends ImperialStarship {}
class SuperStarDestroyer extends ImperialStarship {}

class CorelianCorvette extends RebelStarShip {}
class MonCalamariCruiser extends RebelStarShip {}


Более того мы теперь можем построить верфи

//ну или Shipyard
class ImperialWarf () : ImperialStarship {}
class RebelWarf() : RebelStarship {}
class CorelianWarf() : Starship {}


Однако мир чуть сложнее, да и зло нам нужно не просто так, а чтобы творить, ну… собственно зло.

abstract class Starhip 
  {
  public function shootAt(Starship $target)
  }


И наверняка, просто зла недостаточно, нужно вселенское зло

interface Destroyable {}

abstract class Starship implements Destroyable {}
abstract class Planet implements Destroyable {}

//ее по канону конечно нужно сделать синглтоном, но кто знает....
class DeathStar extends Starship
  {
  public function destroy(Destroyable $target)
  }  


Ну и наведем чуточку красоты

class GalaxyFarFarAway
  {
  private static $planets;

  public static function getPlanetByName(String $planetName) {}
  public static function getRandomHabitablePlanet() {}
  }


И как итог, сферический Люк Скайвокер в вакууме, может теперь:

$myNewShip = new MilleniumFalcon();
$destination=GalaxyFarFarAway::getPlanetByName('Alderaan')

$myNewShip->hyperjumpTo($destination->getCoords());


А губернатор Таркин, мерзко ухмыляясь:

$myPrecious = new DeathStar();
$victim=GalaxyFarFarAway::getRandomHabitablePlanet()

$myPrecious->destroy($victim);


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

При этом: помнить или разбираться сразу во всем необходимости нет вообще никакой. По методам IDE подскажет прямо на ходу. По дереву абстракций ходить тоже легко и просто. Захотели сгенерить документацию? Тоже без проблем.
«губернатор Таркин, мерзко ухмыляясь»

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

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

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

Пример — цифровая фильтрация. Пока у нас только один фильтр, мы можем тупо написать функцию типа int filter(int input_value), а всякие там внутренние данные типа массива для фильтрации методом усреднения или хотя бы одного предыдущего значения для КИХ/БИХ спрятать в локальной области видимости модуля. Даже если нужно менять параметры фильтра на ходу — опять же добавляем функцию, присваивающую переданные ей значения внутренним переменным модуля, задающим коэффициенты, или даже флаги, меняющие тип фильтрации.

По сути, модуль обеспечивает одну из трех фишек ООП — инкапсуляцию. Но у такого модуля есть проблема — он один. Даже если мы размножим наборы хранимых внутренних данных (массив1, массив2, коэффА_1, коэффА_2, коэффБ_1, коэффБ_2), то как указать, с какими из них должна работать функция в каждом случае? Не говоря уже о том, что такое размножение просто неудобно и громоздко.

Окей, путь есть — упаковываем набор в структуру, используем несколько структур (можно даже динамически создавать экземпляры структур в рантайме по мере надобности) и получаем возможность при вызове функции указывать, с какой из структур работать: filter(value, &dataset1). Но это нарушает инкапсуляцию — чтобы передать в функцию указатель или ссылку на структуру, внешний код должен иметь эту структуру в своей видимости.
Хотя и это можно обойти — если наборы заданы статично и их немного, можно сразу присвоить им некие идентификаторы, и передавать функциям эти идентификаторы вместо ссылки/указателя. При динамическом же созданий наборов код еще усложняется — надо будет генерировать идентификаторы, где-то хранить соответствие идентификаторов и адресов структур в памяти и т.п. При том что мы по сути уже «изобрели объект».

И вот тут стоит только перейти от C к С++ и использовать волшебное слово class — и весь этот геморрой оказывается переложен на плечи компилятора, указатель на набор данных объект хранит скрыто внутри себя и снаружи опять виден простой «черный ящик».

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

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

Вот, к примеру, одна моя софтина принимает от железки кучу разных типов данных (даты, числа с довольно разнообразным кодированием и т.п.) и отображает их в интерфейсе, выводит в отчеты и т.п.
Как это делается чисто процедурным путем — для каждого такого значения вызывается функция преобразования в строку — inttostr, datetostr, temperature1tostr, temperature2tostr.
Тут есть две засады. Во-первых, код тупо загромождается кучей вызовов этих функций
label1.Caption = inttostr(parameter1);
edit2.Text = inttostr(parameter2);

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

Не хватает раздела: «а зачем?». Ну серьезно, очень многие так увлекаются, что KISS, YAGNI итд для них не существует, есть только сферический ООП в вакууме. Причем с костылями естественно. Соответственно, растет сложность разработки и поддержки. Как раз то, с чем ООП призван бороться.

Заключение тоже неплохое. Но надо еще сказать, что даже при наличии ооп, многие переходят к классовому программированию. Ну вот холивара ради(ну все равно почти под каждой статьей про ооп такой есть): допустим ваши трансфомеры начали сражаться друг с другом, соответственно нам понадобиться расчитать опыт/здоровье/щиты/прочие штуки, а потом обработать собственно убийство. Тут сразу возникнет вопрос: а где и как? И на этом этапе многие скажут, что нужен класс CombatManager, которому нужно передать трансормеров, вместо optimus->attack(megatron). Вот только про его существование нужно обязательно знать и помнить, ибо IDE в данном случае просто бесполезна.
Не хватает раздела: «а зачем?». Ну серьезно, очень многие так увлекаются, что KISS, YAGNI итд для них не существует, есть только сферический ООП в вакууме.

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

Ну вот холивара ради(ну все равно почти под каждой статьей про ооп такой есть): допустим ваши трансфомеры начали сражаться друг с другом, соответственно нам понадобиться расчитать опыт/здоровье/щиты/прочие штуки, а потом обработать собственно убийство. Тут сразу возникнет вопрос: а где и как? И на этом этапе многие скажут, что нужен класс CombatManager, которому нужно передать трансормеров, вместо optimus->attack(megatron).

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

Да это хорошо, но таки неплохо было бы понимать, при каких условиях она возникает. Это очень мало где поясняется.

Это не относится к ООП, это относится к проектированию. А само ООП — просто один из возможных инструментов реализации вашей архитектуры.

Ловко вы ушли от ответа. Даже возразить нечего)
Это очень мало где поясняется.

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

Потому что, чтобы определить, какая парадигма подойдет для данного проекта лучше, нужно реализовать этот проект в разных парадигмах и сравнить стоимость разработки/поддержки в каждой из них. При этом требуются опытные команды разработчиков для каждой парадигмы. Иначе получится, что ООП-шники начнут пробовать реализовывать проект на ФП, у них получится плохо, и они решат, что ФП отстой, ООП — сила. (Я привел две популярные парадигмы, но их на самом деле много, это тоже учитывайте)

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

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

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

Хочется сразу уточнить. Под полезностью ООП вы имеете в виду пользу наличия/отсутствия ООП или пользу сравнительно с какой-то другой парадигмой? Если второе, то с какой именно?
Опять же всё в зависимости от конкретной ситуации, поэтому и кавычки :)

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

В играх 3-ий и 4-ый уровни абстракции используются. Хотя, может тут лучше выделить еще один уровень, или я сейчас 4-ый уровень опишу, не знаю.
В общем, вводятся понятия «компонент», «контейнер». Нет никаких классов трансформеров, пушек. Есть компоненты, отвечающие за характеристики, способности, различный функционал. После добавления компонентов в объект-контейнер, получается конкретный трансформер, пушка или радар.
К тому же, при таком подходе количество интерфейсов уменьшается раз в 100, т.к. их более эффективно заменяют компоненты. Там, где раньше у класса проверялось наличие интерфейса, будет проверяться наличие компонента.

По поводу 4-го уровня. Не понятно, тут разве не о крафте в играх говорится?)
Еще, похоже что относящееся к этому уровню встречается в играх при написании ИИ. Например, в деревьях поведений. Там составными частями являются действия, условия.
Мне кажется, вы неправильно поняли примеры с уровнями абстракции. Эти 4 уровня придуманы для данной статьи для сферической игры в вакууме. Это не какая-то устоявшаяся в индустрии классификация уровней, поэтому рассматривать все возможные уровни и реализации, не имея на руках ТЗ — бессмысленно.

По поводу 4-го уровня. Не понятно, тут разве не о крафте в играх говорится?)

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

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

Третий: поверх рецептов есть система улучшений или модулей, но рецепты все равно жестко прописаны в игре. Например, добавив редкие материалы, вы можете получить продукт с более высокими характеристиками. Либо можете улучшать продукт дополнительными модулями, добавляя тем самым новый функционал.

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

Это второй уровень по нашей вымышленной классификации. Там список рецептов, который игрок поменять не в силах. Получающиеся продукты никак не улучшаются. Но если для каких-то вещей есть зачарование, то тогда третий уровень.
И всё же там есть 4-й уровень, если не 5-й :)
Насколько я знаю, рецепта на процессор в minecraft нет.

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

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

А во-вторых, не соглашусь, что процессор — это пятый уровень. Скорее второй:
1 уровень — это блоки.
2 уровень — это прочность блоков и NBT-тэги.
3 уровень (которого нет без модов) — возможность в блоки вставлять модификаторы.
4 уровень (тоже отсутствует) — возможность создавать новые блоки с новыми названиями и свойствами.

Процессор работает полностью на блоках и их состояниях, именно поэтому он такой огромный и не масштабируемый, то бишь не гибкий. Вот если бы этот процессор можно было бы создать в виде нового блока и дать ему название «процессор», то да, это был бы четвертый уровень.
Ну… это уже вопрос точности терминов. Если говорить в рамках того, что описано в статье — то упомянутый процессор всё артефакт, созданный игроком из винтиков и гаек, которые представлены в игре. Причём ему, как и авторам многих самостоятельных артефактов, пришлось крафтить одни блоки из других, потом соединять их, создавая нечто новое, чего не было в рецептах, а потом эти новый элементы снова соединять… Это как раз 4-й уровень.
А если из этих артефактов создаётся новая «реальность», в исходной игре, или игра в игре, и исходная механика позволяет это — тут уже и 5-й уровень.

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

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

это если навесить два ограничения

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

Чтобы понять мою мысль, попробуйте описать архитектуру окружающего мира, в котором собрали процессор, с точки зрения разработчика программных сущностей и их ООП-отношений.
Я рассматривая вопрос этих абстракций проще. В классических играх с крафтом есть до 3-х уровней:
1 — базовые элементы, данные as-is
2 — крафт предметов/навыки персонажа
3 — аугментация характеристик персонажа/предметов.
Все три уровня укладываются не только в механики, но и любой вариант наверняка есть в некой «базе сущностей» на этапе разработки. Иначе говоря, чтобы не пытался создать игрок — это уже есть в БД игрового мира.

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

В играх-песочницах есть некий элемент мета-программирования, который позволяет создавать самостоятельные игровые сущности. Дальше вопрос лишь в том, как это реализовано и как много свободы у игрока.
Есть игры, где сущности рандомно генерируются из большого числа «винтиков», как в No Man’s Sky, или где игроку изначально даны большие возможности по созданию неведомого, как в Spore
Давайте немного детализируем ваш пример с яваскриптом, чтобы уловить разницу. Представьте, что из яваскрипта уберут функции (как сам механизм). Можно ли на нем будет написать все программы, которые уже написаны? Несомненно, да, и они даже будут работать. Но код превратится в плоскую систему копипаст, участки кода можно будет подписать комментариями, но невозможно будет вызвать из других участков наравне с остальными инструкциями. Проекты перестанут быть гибкими, масштабируемыми и расширяемыми.

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

Возможность в игре соорудить какие-то сложные механизмы еще не говорит о гибкости архитектуры. Богатые возможности могут быть обусловлены банально большим количеством строго регламентированных возможностей с разделением на 2-3 уровня абстракции. Но богатые != гибкие.

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

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

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

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

Вот я товарищу именно это пытаюсь донести уже сколько комментов подряд.
Если у нас есть в коде игры на первом уровне сущности A, B и С, и если в самой игре A+B+C == ABC, то это не новый уровень, а просто расширение текущей плоскости.

Новый уровень — это A+B+C == D при тех же условиях, что D не прописан в коде игры, а генерируется действиями игрока на лету.

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

Если вы не видите разницу между этими двумя принципиально разными классификациями, это не значит, что ее нет.

А пример с яваскриптом вы, похоже, даже не попытались понять. Он идеально демонстрирует, как убирание слоев (которое вы как то не так понимаете) делает продукт ригидным и неудобным, но с сохранением богатых возможностей. Что мы и видим с процессором в майнкрафте: отсутствие необходимых слоев в игре не лишает игру возможности построить процессор, но превращает это мероприятие в подвиг. Как и любую программу на яваскрипте без функций.
Я понимаю, о чём вы толкуете, но тут уже больше вопрос к тому, что как назвать.
С точки зрения игрока, не известно и не важно, вставляет он в слот композицию (A+B+C) или созданный новый компонент D.
Но если смотреть на то, что написано в статье — то главное отличие — это возможность любым способом создать такое нечто, которого нет в кодовой базе изначально.

В вашем примере про яваскрипт функции — это лишь удобная абстракция для написание кода, сиречь манипуляции данными и внешними/внутренними АПИ. Чем окажутся функции после компиляции — знает только конечный движок.
Есть игры, где доступны скриптовые модификации. Куда вы их отнесёте? С одной стороны, комбинация изначальных модулей и кода — явно что-то новое, т.е. D. Но во внутренней базе это может храниться как модуль+код, т.е. именно как (A+B+C).

Я вот к чему клоню — дайте простое, но исчерпывающее объяснение, что по-вашему будет 4-м уровнем.
Модули/субмодули/детальки не дают такого объяснения.
А мне кажется, дело даже не в количестве уровней, а в принципе их связи. Кажется, автор свалил это в кучу. Давайте отделим мух от котлет.

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

Тут есть два варианта:

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

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

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

Говоря про майнкрафт, тут есть две таких «сборочных цепочки», и в иерархию они связаны.

Есть крафт предметов и блоков по рецептам — он целиком первого типа. Список создаваемых объектов и их свойств жестко задан.

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

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

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

(переписываю эту телегу уже часа три, чувствую какие-то сомнительные моменты, но не могу сформулировать)
Нет, погодите, перестаньте рассматривать эти 4 уровня как постулат. Процитирую статью:
Давайте рассмотрим определение уровня абстракции из возможных вариантов на примере гипотетической игры «трансформеры-онлайн».

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

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

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

Теперь еще раз объясняю, что значит идут параллельно.

Скрафтив какой-то блок, и поставив его в мире в виде блока, этот блок переходит из системы крафта (с одной иерархией слоев) в систему мира (с другой, своей иерархией уровней). Если бы иерархии этих двух механик складывались, то блок в игровом мире имел бы не только ID, но и ссылки на компоненты, из которых его скрафтили. А свойства блока определялись бы свойствами этих самых дочерних объектов.

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

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

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

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

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

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

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

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

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

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

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

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

В целом в eve online все довольно бодренько с быстродействием. В одной битве могут принимать участие тысячи кораблей
Я, как бы в курсе, what is :)
Хранить таблицы расчётов характеристик — дёшево. Даже если их в realm миллионы, на одну локацию приходят тысячи. При этом остаётся только рассчитывать параметры взаимодействия. Так что хранить надо не все возможные комбинации, а только тот набор, который есть в конкретном объекте.

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

И нагрузка все таки смешная, даже без кэширования, даже для тысячи кораблей. Мой домашний процессор такое может рассчитывать без проблем, и у него еще останется, чтоб крипту майнить. А лагает оно, я уверен, из-за IO (сеть, база, и т. д.), но уж точно не из-за CPU.
Кэш — это один из вариантов хранения. Суть не в том.
Ещё раз — игровые сервера обслуживают огромную массу событий, и для всякой онлайн-мясорубки главная проблема — это инпут-лаг. Проектировщики обычно стараются избавиться от любых действий, которые удлиняют цепочку обработки. Вы ещё учтите, что эти расчёты надо синхронизировать по времени между всеми участниками события.
Но… мы как-то сильно удалились от темы :)
Вот теперь стало ясно, в чём наше разногласие. Говоря про уровни абстракции в конкретной игре вы имели в виду предположения об ООП-модели. Хотя не известно, как там реализованы составные блоки.

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

Но если не думать о конкретных оптимизациях и предположить, что реализация сделана именно так, как она видна снаружи, то ничто не мешает подумать, что один конкретный мир в minecraft — один объект, композиция из составных элементов, между которыми ещё и взаимодействие сложное. А ещё можно разделить большой мир на фрагменты по активным связям. Т.е. на кучки блоков, не имеющих активностей между кучками.
Да вообще много чего можно предположить :)
Функционал продукта при этом полностью формируется из свойств деталек и их комбинаций.

Таким образом генерируется оружие в серии игр Borderlands. Правда, игроку этот механизм внутри игры недоступен, только в виде чита — редактора сейвов.
При этом слова в названии тоже являются «детальками» и влияют на свойства. Есть вопрос совместимости деталек.
Вот здесь можно поперебирать варианты blmodding.wikidot.com/gear-calculator
Ага, не правильно понял. Спасибо за разъяснение!
Крайне трудно найти толковые вводные по ооп для тех кто уже освоил мозиловские азы, но не имеет представления как и где это применить на практике. Очень полезная статья, многие нюансы, которые даже не знаешь как поисковом запросе прописать разъяснились сами собой. Прям респектую.
Отличное изложение материала! Однозначно полезно для новичков и профессионалов!
Даже зная все выше изложенное, было очень приятно читать.
Картинки топ, использование трансформеров топ, прям каеф! Спасибо за статью!
Очень уместно, что указаны синонимы к терминам или похожие механизмы. С каждым выученным языком переосмысливаешь концепты, например, для меня оказалось примечательным прототипнное наследование в JS и Lua, интерфейсы лучше раскрылись в Go.
И некоторые отличия теории от практики.
Например, часто создаётся только 1 объект класса (не про Singletone) есть странное ощущение зачем городил целый класс, а создал только один инстанс.
Также полях класса тоже хранят данные (например ссылки на все сущности, чтоб их потом всех по удалять) и вызывают функции класса без создания объекта.

Дополнительный вопрос: что лучше — наследование или некое специальное поле, где хранится признак принадлежности объекта некоторому классу? Можно ли вместо того, чтобы создать два различных класса (Автоботы) и (Десептиконы), иметь некую метку, кто есть, кто? Тут ещё вопрос в чём? Если есть такая метка, то можно добавить в список возможных значений новое, то есть — добавить новый класс. Иначе придётся создавать код нового класса и перекомпиллировать приложение.

Если есть такая метка, то можно добавить в список возможных значений новое, то есть — добавить новый класс.

А поведение, связанное с этим новым классом, вы как добавлять собираетесь?


А то если его нет, то у вас и нового класса нет, на самом деле...

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации