В данной статье рассматривается один из подходов к следующей ступени развития ООП (объектно-ориентированного программирования). Классический подход к ООП строится на концепции наследования, что в свою очередь накладывает серьезные ограничения по использованию и модификации уже готового кода. Создавая новые классы, не всегда получается наследоваться от уже существующих классов (проблема ромбовидного наследования) или модифицировать существующие классы от которых уже унаследовалось множество других классов (хрупкий (или чрезмерно раздутый) базовый класс). При разработке языка программирования Delight был выбран альтернативный подход для работы с классами и их композицией - КОП (компонентно-ориентированное программирование). Начать следовало бы с основ языка, его синтаксиса и правил работы КОП. Но это довольно скучно, так что сразу перейдем к конкретному игровому примеру. Для понимания всего нижеизложенного, требуется полноценное знание ООП, так как сама композиция строится на тех же принципах что и ООП. Более детально о том, как это все-таки работает, можно прочитать в следующем после этого разделе. Рассмотрим пример из условной игры, где некие существа могут передвигаться по карте. Напишем код поведения этих существ. Начнем с базовых классов. BaseBehavior - отвечает за базовое поведение существа, сам класс не имеет логики, только необходимые декларации.  PathBuilder - класс который отвечает за поиск пути по земле (включая обход препятствий).  Модификатор [shared] означает что это поле будет общим для всех подклассов финального класса. Дальше пропишем классы, в которых уже есть непосредственно логика поведения: Здесь все просто: SimpleBehavior - существо будет перемещаться по карте по случайным координатам. AgressiveBehavior - если игрок находится близко, то существо бежит к нему. Иначе управление передается в SimpleBehavior.  ScaredBehavior - если игрок находится недалеко, то существо отбегает от него или двигается согласно SimpleBehavior.  open - означает открытое поле класса заданного типа но без имени.  fne - перегрузка (override) виртуальной функции.  nextFn - виртуальный вызов следующей в цепочке функции. Пока что весь код можно отобразить и с помощью обычного ООП, но для следующего поведения используем КОП: Здесь уже включается "магия" композиции. В этом простом коде, при вызове DoTurn, управление сначала передаётся в AgressiveBehavior.DoTurn. Если игрок близко, то существо побежит к нему. Если нет, то управление переходит к ScaredBehavior.DoTurn - если игрок недалеко, то существо убегает от него. Если нет, то дальше вызывается SimpleBehavior.DoTurn и существо просто бродит по карте. На этом коде уже можно создать существ Волка (AgressiveBehavior), Зайца (ScaredBehavior) и Кошку (UncertainBehavior). Но что делать для других видов существ? Летающих или плавающих? Или комбинированных? В ООП подобная иерархия уже не сработает. Зато очень помогает композиция. Сначала создадим новые классы для поиска пути в разных средах: А дальше просто подменим этими классами уже существующий код поведения: В этом классе "Акулы", сначала создаётся класс поиска пути по воде, дальше используется код с AgressiveBehavior, только учитывая, что класс PathBuilder общий (shared), то в AgressiveBehavior (как и в SimpleBehavior) будет использоваться PathBuilder_water (так как он был объявлен ранее чем обычный PathBuilder). Соответственно вся логика AgressiveBehavior сохранилась, но поиск пути будет работать уже по воде. Таким же способом, просто перебирая классы-компоненты и используя минимум кода, можно создать существ с разным поведением в разных средах обитания: Как видим, суть компонентно-ориентированного программирования состоит в создании небольших классов-компонентов и правильной комбинации этих классов в финальном объекте-сущности. Объявление простого класа в Delight выглядит следующим образом: Здесь val является обычным членом класса, с типом OtherClass. Функции, как и в ООП языках могут быть виртуальными, для этого используется модификатор [virtual] и перегружаться/дополняться с помощью ключевого слова fne (вместо fn) А вот синтаксис наследования (вернее композиции) сильно отличается от классических языков. В случае, если класс хочет перегрузить функцию своего базового класса, он должен объявить базовый класс с модификатором [shared] (общий), и использовать fne для перегрузки функции: Ключевое слово nextFn вызовет следующую функцию в цепочке виртуальных вызовов. Для примера похожий (но не эквивалентный) код на С++ В классе может быть множество полей с модификатором [shared], что соответствует концепции множественного наследования. Более того, shared поля одного типа могут повторяться в любом месте иерархии класса, но при этом в финальном объекте, независимо от количества [shared] деклараций одного типа, создастся только один общий объект этого типа, а все [shared] поля соответствующего типа во всех общих классах будут содержать только ссылки на этот объект (вернее запись в vtable). Таким образом в коде: при создании класса ClsC, этот объект будет содержать три базовых объекта в единичном экземпляре (Base, ClsA, ClsB) и значение val будет всегда одинаковым (общим) для всех этих объектов. Подобный подход соответствует виртуальному наследованию классов, которое используется в С++. Как видно из синтаксиса, у общих полей при композиции задаются также имена (в отличие от классического ООП, где при наследовании, достаточно указать тип), и последующее обращение к такому полю должно начинаться с имени поля. Однако в Delight есть синтаксический сахар в виде декларации полей через ключевое слово open (делает поле открытым). Для компилятора это ничего не меняет, но программисту не нужно будет каждый раз обращаться к такому полю по имени (все поля и функции открытого класса могут быть доступны просто через this). Поскольку такая декларация классов представляет собой зацикленный граф, то обход и построение финального класса будет сложнее чем построение дерева иерархии в ООП. По сути, при строительстве класса используются следующие правила: Класс проходится сверху вниз, если находится новый общий (shared) класс, то происходит строительство этого класса; Если в поле общий (shared) класс такого типа уже был построен, то используется указатель на уже построенный класс. Таким же способом строится цепочка вызовов виртуальных функций, но с двумя важным деталями:  В цепочку вызовов сначала попадают функции из тела текущего класса, и только после них функции из общих полей класса; Декларация функции всегда будет стоять в конце цепочки вызовов. Для вызова следующей в цепочке виртуальной функции, используется оператор nextFn. Важно понимать, что этот оператор по сути является виртуальным вызовом (virtual call), в отличие от статического вызова перегруженной функции в классическом ООП (inherited call). Например, такой код: выдаст: Такой подход позволяет перегружать функции одного класса, другим, находясь при этом на одном уровне иерархии. Благодаря этому, классы могут состоять из готовых компонентов, которые перекрывают или дополняют чужие функции, что существенно облегчает композицию кода. При композиции, основной функционал финального класса разбит на несколько базовых кирпичиков, комбинация которых и дает нужные результаты. Также Delight поддерживает статическую композицию кода, но это уже материал для другой статьи.