От переводчика
Привет, Хабр!

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

Перевод публикую с разрешения сайта Toptal, где выложен оригинал автора Eduardo Dias da Costa.

Обычно программисты знакомятся с профессией, начиная с Hello World. Затем ставят всё большие и большие цели и каждая новая задача приводит к важному уроку: чем больше проект, тем запутаннее код.


image

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


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


image

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


В этом обучающем материале я расскажу о своём опыте работы с популярным игровым движком Unity3D и применении шаблона Модель-Представление-Контроллер (Model-View-Controller, MVC). За семь лет работы и борьбы со спагетти-кодом в игровых проектах, я добился отличной структуры кода и скорости разработки при его использовании.


Начну с разъяснения о базовой архитектуре Unity: шаблоне Сущность-Компонент (Entity-Component, EC), а затем расскажу, как поверх него строится MVC. И приведу в пример маленький проект-макет.


Мотивация


Чтобы адаптировать существующие шаблоны проектирования под конкретную задачу, программисты вынуждены изменять их. Эта свобода в программировании — доказательство, что мы не нашли единственно верной архитектуры софта. Эта статья тоже не подразумевается, как решение всех проблем. Она показывает возможности двух хорошо известных шаблонов проектирования: EC и MVC.


Шаблон Сущность-Компонент


Сущность-Компонент (EC) — шаблон проектирования, где первым делом определяется иерархия элементов, из которых состоит приложение (Сущности), а затем указываются логика и данные для каждого из них (Компоненты). В «программерских» терминах, Сущность может являться объектом с массивом из нуля и более Компонентов. Опишем её так: some-entity [component0, component1, ...]


Пример простого дерева EC:


- app [Application]
   - game [Game]
      - player [KeyboardInput, Renderer]
      - enemies
         - spider [SpiderAI, Renderer]
         - ogre [OgreAI, Renderer]
      - ui [UI]
         - hud [HUD, MouseInput, Renderer]
         - pause-menu [PauseMenu, MouseInput, Renderer]
         - victory-modal [VictoryModal, MouseInput, Renderer]
         - defeat-modal [DefeatModal, MouseInput, Renderer]

EC хороший шаблон для нивелирования проблем множественного наследования, когда запутанная структура классов может порождать проблемы вроде проблемы бриллианта (the diamond problem): класс D, наследуемых от B и C с общим классом A, может содержать конфликты из-за разного переопределения возможностей A классами B и C.


image

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


Если разбить задачи и обработчики данных на небольшие Компоненты, они могут присоединяться (attach) к Сущностям и переиспользоваться без множественного наследования, которого нет в C# и JavaScript, основных языка программирования Unity.


Где Сущность-Компонент не оправдывает ожиданий


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


image

Один из способов избежать бардака — следовать дополнительным принципам поверх EC. Я разделяю программу на три категории:


  • Одни обрабатывают сырые данные и позволяют создавать, получать, обновлять, удалять и искать их (т.е. CRUD);
  • Другие реализуют интерфейсы (пользовательские, а не программные, прим. пер.) для взаимодействия с другими элементами, детектируют события, связанные с их зоной ответственности, и рассылают уведомления об этих событиях;
  • Третьи отвечают за получение этих уведомлений, реализуют бизнес-логику и решают, как изменить данные.

К счастью, уже существует шаблон проектирования, описывающий это поведение.


Шаблон Модель-Представление-Контроллер (MVC)


Модель-Представление-Контроллер разделяет программу на три основных компонента: Модель (CRUD), Представление (Интерфейс/Детектирование) и Контроллер (Решение/Действие). MVC достаточно гибок и реализуется поверх EC и OOP.


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


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


Unity и EC


Теперь поближе познакомимся с возможностями Unity.


Unity — основанная на EC платформа, где Сущностями предстают инстансы GameObject, а возможность сделать их видимыми, двигающимися и т.д. обеспечивается наследниками класса Component.


Панель иерархии (Hierarchy Panel) и Инспектор (Inspector Panel) — мощный инструмент по сборке приложения, закрепления Компонентов за Сущностями, конфигурации их инициализирующего состояния и начальной загрузки игры. Без них потребовалось бы куда больше кода.


image

Панель иерархии (Hierarchy Panel) с четырьмя GameObject справа.


image

Инспектор (Inspector Panel) с компонентами GameObject.


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


Однако, MVC выручит нас: разделим Сущности по их назначению и структурируем приложение, как показано на скриншоте:


Адаптация MVC для геймдева


Пришло время для двух модификаций общего шаблона MVC, чтобы адаптировать его к ситуациям, специфичным для Unity с MVC:


  • Ссылки на классы MVC должны легко пробрасываться по коду.
    • Чтобы дать другому объекту доступ к инстансу, в Unity, разработчику приходится в редакторе перетаскивать (drag-n-drop) ссылки на объекты или использовать громоздкие (и медленные, прим. пер.) вызовы GetComponent(...).
    • Если Unity крашнулась или из-за какого-то бага разорвались выстроенные в редакторе ссылки, начинается сущий Ад.
    • Из-за этого необходим корневой объект, через который можно получить доступ ко всем инстансам, используемым в Приложении.
  • Некоторые элементы инкапсулируют логику, не относящуюся ни к одной из категорий MVC и часто переиспользуются. Я назову их Компонентами. Они же являются Компонентами в структуре Сущность-Компонент, но в MVС фреймворке выступают просто вспомогательными классами.
    • Пример: Компонент Rotator, который просто поворачивает объект. Но не шлёт уведомлений, ничего не хранит и не содержит бизнес-логику.

Для решения этих проблем я модифицировал оригинальный шаблон и назвал его AMVCC или Приложение-Модель-Представление-Контроллер-Компонент (Application-Model-View-Controller-Component).
image


  • Application — единая точка входа в приложение и контейнер для всех критичных инстансов и зависимых от приложения данных.
  • MVC — теперь ты знаешь, что это такое :)
  • Component — небольшой самодостаточный и легко переиспользуемый скрипт.

В моих проектах, этих двух нововведений хватает с лихвой.


Пример: 10 Bounces


Применим шаблон AMVCC на маленькой игре, назовём её «10 Bounces». Установки игры просты: Ball со SphereCollider и Rigidbody, который начнёт падать при старте игры; Cube в качестве земли и 5 скриптов для создания AMVCC.


Иерархия


Прежде чем приступить к коду, я сделаю набросок иерархии классов и ассетов, следуя стилю AMVCC.
image


GameObject view содержит все визуальные элементы и View скрипты. Объекты model и controller в маленьких проектах обычно содержат только один соответствующий скрипт, а в проектах побольше в них будет множество скриптов, ответственных за конкретные действия, данные и т.д.


Когда кто-нибудь хочет получить доступ к:


  • Данным: последует в application > model > ...
  • Логике/Процессу работы (workflow): application > controller > ...
  • Рендер/Интерфейс/Детектирование: application > view > ...
    Если все команды будут следовать этим трём правилам, старые (legasy) проекты не будут проблемой.

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


Написание скриптов


Обрати внимание: скрипты, показанные ниже — обобщение реального кода. Если хочешь изучить вопрос подробнее, вот ссылка на мой MVC фреймворк Unity MVC. Он структурирован под AMVCC, поэтому там ты найдёшь базовые классы, необходимые в большинстве приложений.

Теперь взглянем на структуру скриптов «10 Bounces».


Для незнакомых с устройством Unity, я кратко поясню, как взаимодействуют GameObjects. «Компоненты» из шаблона Сущность-Компонент представлены классом MonoBehaviour. Чтобы он стал доступен во время работы приложения, разработчик должен в редакторе перетащить (drag-n-drop) исходный файл на GameObject (который является «Сущностью» в шаблоне EC) или использовать команду AddComponent<YourMonobehaviour>(). После этого скрипт будет инстанцирован и готов к использованию.


Объявим два класса.


Application («A» в AMVCC) — основной класс, у которого будет только один инстанс, содержащий ссылки на все инстанцированные в игре элементы. Внутри объявим три публичные переменные: model, view и controller, которые обеспечат доступ к корневым объектам MVC.


Element — вспомогательный базовый класс, дающий дочерним инстансам MVC доступ к Application.


Помни, что оба класса — наследники MonoBehaviour. Они — «Компоненты», прикреплённые к GameObject «Сущностям».


// BounceApplication.cs

// Base class for all elements in this application.
public class BounceElement : MonoBehaviour
{
   // Gives access to the application and all instances.
   public BounceApplication app { get { return GameObject.FindObjectOfType<BounceApplication>(); }}
}

// 10 Bounces Entry Point.
public class BounceApplication : MonoBehaviour
{
   // Reference to the root instances of the MVC.
   public BounceModel model;
   public BounceView view;
   public BounceController controller;

   // Init things here
   void Start() { }
}

Отнаследуем от BounceElement базовые классы MVC. Обычно BounceModel, BounceView и BounceController выступают контейнерами специализированных MVC объектов, но в упрощённом примере только у Представления будет вложенная структура. Модели и Контроллеру хватит по одному скрипту.


// BounceModel.cs

// Contains all data related to the app.
public class BounceModel : BounceElement
{
   // Data
   public int bounces;  
   public int winCondition;
}

// BounceView .cs

// Contains all views related to the app.
public class BounceView : BounceElement
{
   // Reference to the ball
   public BallView ball;
}

// BallView.cs

// Describes the Ball view and its features.
public class BallView : BounceElement
{
   // Only this is necessary. Physics is doing the rest of work.
   // Callback called upon collision.
   void OnCollisionEnter() { app.controller.OnBallGroundHit(); }
}

// BounceController.cs

// Controls the app workflow.
public class BounceController : BounceElement
{
   // Handles the ball hit event
   public void OnBallGroundHit()
   {
      app.model.bounces++;
      Debug.Log(“Bounce ”+app.model.bounce);
      if(app.model.bounces >= app.model.winCondition)
      {
         app.view.ball.enabled = false;
         app.view.ball.GetComponent<RigidBody>().isKinematic=true; // stops the ball
         OnGameComplete();
      } 
   }

   // Handles the win condition
   public void OnGameComplete() { Debug.Log(“Victory!!”); }
}

Все скрипты созданы, теперь можно закрепить их за GameObjects и сконфигурировать.


Иерархия будет такой:


- application [BounceApplication]
    - model [BounceModel]
    - controller [BounceController]
    - view [BounceView]
        - ...
        - ball [BallView]
        - ...

Рассмотрим, как это выглядит в редакторе Unity на примере BounceModel:
image
BounceModel с полями bounces и winCondition.


После установки скриптов и запуска игры, вывод в Консоль будет таким:
image


Уведомления (notifications)


Когда шар ударяется о землю, его представление вызывает метод app.controller.OnBallGroundHit(). Нельзя сказать, что это «неправильный» способ слать уведомления в приложении, но по моему опыту куда удобнее использовать простую систему нотификаций, реализованной в классе Application.


Обновим BounceApplication:


// BounceApplication.cs

class BounceApplication 
{
   // Iterates all Controllers and delegates the notification data
   // This method can easily be found because every class is “BounceElement” and has an “app” 
   // instance.
   public void Notify(string p_event_path, Object p_target, params object[] p_data)
   {
      BounceController[] controller_list = GetAllControllers();
      foreach(BounceController c in controller_list)
      {
         c.OnNotification(p_event_path,p_target,p_data);
      }
   }

   // Fetches all scene Controllers.
   public BounceController[] GetAllControllers() { /* ... */ }
}

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


// BounceNotifications.cs

// This class will give static access to the events strings.
class BounceNotification
{
   static public string BallHitGround = “ball.hit.ground”;
   static public string GameComplete  = “game.complete”;
   /* ...  */
   static public string GameStart     = “game.start”;
   static public string SceneLoad     = “scene.load”;
   /* ... */
}

Благодаря этому, разработчик сможет открыть один файл и понять общее поведение приложения, вместо того, чтобы искать по коду методы вроде controller.OnSomethingComplexName.


Теперь адаптируем BallView и BounceController для работы с новой системой.


// BallView.cs

// Describes the Ball view and its features.
public class BallView : BounceElement
{
   // Only this is necessary. Physics is doing the rest of work.
   // Callback called upon collision.
   void OnCollisionEnter() { app.Notify(BounceNotification.BallHitGround,this); }
}

// BounceController.cs

// Controls the app workflow.
public class BounceController : BounceElement
{
   // Handles the ball hit event
   public void OnNotification(string p_event_path,Object p_target,params object[] p_data)
   {
      switch(p_event_path)
      {
         case BounceNotification.BallHitGround:
            app.model.bounces++;
            Debug.Log(“Bounce ”+app.model.bounce);
            if(app.model.bounces >= app.model.winCondition)
            {
               app.view.ball.enabled = false;
               app.view.ball.GetComponent<RigidBody>().isKinematic=true; // stops the ball
               // Notify itself and other controllers possibly interested in the event
               app.Notify(BounceNotification.GameComplete,this);            
            }
         break;

         case BounceNotification.GameComplete:
            Debug.Log(“Victory!!”);
         break;
      } 
   }
}

В проектах побольше может быть много уведомлений. Чтобы избавиться от огромных switch-case, целесообразно создавать специализированные контроллеры и обрабатывать в них разные группы уведомлений.


AMVCC в реальном мире


«10 Bounces» показывает простейший вариант использования шаблона AMVCC. Чтобы использовать его на практике, придётся оттачивать мышление в рамках трёх категорий MVC и учиться визуализировать сущности в виде упорядоченной иерархии.


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


Практические правила (от Эдуардо)


Это не «Универсальный гид по организации MVC», а набор простых правил, которые помогают мне разделить Модель, Представление и Контроллер.


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


Разделение классов


Модели


  • Содержат основные данные приложения вроде здоровья или запаса патронов игрока.
  • Сериализуются, десериализуются и/или конвертируются между типами.
  • Загружают/сохраняют данные (локально или по Сети).
  • Сообщают Контроллерам о прогрессе операции.
  • Хранят состояние конечного автомата игры.
  • Никогда не вызывают Представления.

Представления


  • Могут получать данные из Моделей для отображения текущего состояния игры. Например, метод Представления player.Run() может использовать model.speed для наглядного отображения способностей игрока.
  • Никогда не изменяет Модели.
  • Строго следует назначению класса. Например:
    • PlayerView никогда не детектирует ввод или изменение состояния игры.
    • Представление работает как чёрный ящик с интерфейсом и оповещает о важных событиях.
    • Не хранит важных данных вроде скорости, здоровья, жизней и так далее.

Контроллеры


  • Не хранят основные данные игры.
  • Могут фильтровать уведомления от Представлений.
  • Обновляют и используют данные Моделей.
  • Управляют действиями на сцене Unity.

Иерархия классов


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


Например, Модель с данными игроков имеет множество переменных playerDataA, playerDataB ,… или Контроллер, обрабатывающий уведомления игрока, — методы OnPlayerDidA, OnPlayerDidB,… Мы хотим уменьшить количество кода и избавиться от префиксов player и OnPlayer. Так как Модель проще для понимания, я продемонстрирую это на её примере.


Я обычно начинаю с единственной Модели, которая содержит все данные игры:


// Model.cs

class Model
{
   public float playerHealth;
   public int playerLives;

   public GameObject playerGunPrefabA;
   public int playerGunAmmoA;

   public GameObject playerGunPrefabB;
   public int playerGunAmmoB;

   // Ops Gun[C D E ...] will appear...
   /* ... */

   public float gameSpeed;
   public int gameLevel;
}

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


// Model.cs

class Model
{
   public PlayerModel player;  // Container of the Player data.
   public GameModel game;      // Container of the Game data.
}

// GameModel.cs

class GameModel
{
   public float speed;         // Game running speed (influencing the difficulty)
   public int level;           // Current game level/stage loaded
}

// PlayerModel.cs

class PlayerModel
{
   public float health;        // Player health from 0.0 to 1.0.
   public int lives;           // Player “retry” count after he dies.
   public GunModel[] guns;     // Now a Player can have an array of guns to switch ingame.
}

// GunModel.cs

class GunModel
{
   public GunType type;        // Enumeration of Gun types.
   public GameObject prefab;   // Template of the 3D Asset of the weapon.
   public int ammo;            // Current number of bullets
   public int clips;           // Number of reloads possible
}

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


А если информация об оружии содержится в одном классе в переменных вроде gun0Ammo, gun1Ammo, gun0Clips и т.д., тогда, столкнувшись с необходимостью сохранить данные Gun, понадобится сохранить Model целиком, включая нежелательные данные о Player. В этом случае очевидно, что будет лучше создать новый класс GunModel.
image
Улучшение иерархии классов


Как всегда, есть две стороны медали. Иногда излишнее разделение по категориям усложняет код. Только опыт поможет наилучшим образом структурировать MVC в твоём проекте.


Открыт новый навык геймдева: Unity с MVC.

Заключение


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


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


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

Комментарии (9)


  1. KyHTEP
    17.04.2016 21:45

    Интересно, Эдуардо понимает, что он просто использует две хороших методики в проекте? MVC и Microservices.

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

    Я это к чему. Что AMVCC попахивает ЧСВ )


    1. AlmazDelDiablo
      17.04.2016 21:58

      Мне показалась интересным организация MVC на уровне объектов в дереве EC. Я с такой практикой никогда раньше не встречался, поэтому решил поделиться с Хабром. Да и от микросервисов тут разве что эвент-шина.


      1. KyHTEP
        17.04.2016 22:01

        Да я и не говорил, что статья бесполезная, если вы так подумали. Я про «еще один стандарт» и ЧСВ )
        А микросервисы в данном случае это компоненты. Т.е. сущность выполняющая узкую специализацию.


        1. AlmazDelDiablo
          17.04.2016 22:11

          Можете указать на место, где упоминается, что AMVCC — стандарт? Скорее всего, я что-то неправильно перевёл.

          Я бы назвал компонент микросервисом. Всё-таки компонент — это компонент. А микросервис я себе представляю, как изолированную программу с web- и/или rpc- интерфейсом. Игра же — монолит. Поправьте, если я не прав, мне роднее и привычнее веб.


          1. KyHTEP
            17.04.2016 22:37

            Я плохо выразился. «еще один стандарт» — это ироничная картинка, применительно к «еще одному» шаблону проектирования. )

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

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

            Да и, честно говоря, мне кажется Эдуардо наслаивает свое виденье игрового движка на уже готовый.
            При проектировании двигла обычно закладываются сценарии использования. Ну и как правило попытка охватить всё-всё-всё. Вот только способы решения одних и тех же задач, в разных движках разные. Ну слава богу сейчас более менее все сошлись, что игровой объект, рендер и физический объект это три большие разницы и их можно и нужно считать раздельно. Давным давно игровой объект содержал что то типа AmmoCount, Parent(в контексте SceneGraph) и Velocity.


            1. AlmazDelDiablo
              17.04.2016 23:56

              Я плохо выразился. «еще один стандарт» — это ироничная картинка, применительно к «еще одному» шаблону проектирования. )

              Почему-то я волнуюсь после публикации и не могу с первого раза правильно воспринять иронию)

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

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


              1. KyHTEP
                18.04.2016 00:20

                Вспомню — обязательно отпишусь (в голове крутятся такие вещи как «декомпозиция» и «изолированные компоненты»).

                Но надо понимать, что каждая область обрастает своими терминами, описывающими одну и ту же сущность. Ну чтобы холивара не было ) В радиоэлектронике это будет что то свое. В кулинарии та же «петрушка». Вот даже на уровне OS, в юникс-системах одной из фундаментальных идей были небольшие тулзы, общающиеся через разные каналы связи, от stdout до pipe. Вот ping, например, как назвать? Измерительная тулза или микросервис (если вкладывать тот смысл, что я описал). В мире скоростных вычислений Intel® Math Kernel Library библиотека или «микросервис» на уровне кода. Вот к какой-нибудь SDK приписать такие свойства уже не получится.


  1. HexGrimm
    18.04.2016 14:49
    +3

    На мой взгляд, статья пестрит вредными советами:
    — Дополнительное наследование от BounceElement не есть хорошо, да и получение ссылок таким образом такие же минусы влечет за собой как и Service Locator.
    — Зачем держать в сцене классы логики как MonoBehaviour, если можно создать их нормально.
    — При росте проекта либо увеличится количество классов контроллера, и появится много связей многие ко многим, так как о всех знают все, либо контроллеры разрастутся и придется перечитывать главу про SOLID.
    Для примера того как можно писать, конечно подходит.


    1. lexxpavlov
      22.04.2016 14:51

      >Зачем держать в сцене классы логики как MonoBehaviour, если можно создать их нормально
      Например, модели сделаны как MonoBehaviour для возможности изменять значения прямо в редакторе.
      А контроллеры можно и не делать MonoBehaviour.

      А вот то, что свойство app в BounceApplication не кэширует ссылку — это странно. Ну и много чего, что можно было бы улучшить, хотя бы — служебные классы сделать абстрактным, события в BounceNotification — не строковые, перечисления, и т.п.