Используются ли в розничных играх «инверсия контроля» и «внедрение зависимостей»?

30

Многие из наиболее усердных разработчиков программного обеспечения, которых я знаю, переходят к инверсии управления и внедрению зависимостей для обработки ссылок на объекты. Исходя из перспективы Flash-игр, я не знаю все тонкости студий AAA, поэтому: используются ли они в мире розничных игр?

Iain
источник
Я думаю, что в этом мире производительность важнее всего, так как плохой код больше пострадает больше пользователей, чем программисты.
Барт ван Хейкелом
Внедрение зависимостей звучит как система, основанная на компонентах, но не могли бы вы привести пример инверсии управления?
АБР
Поскольку большая часть этой ветки, кажется, дезинформирована о том, что такое IoC и что он решает, а ОП на самом деле не спрашивает об этом, в первую очередь я укажу
Борис Калленс
Да, они делают. На самом деле, Рэйр рассказал о том, как они проводят тестирование в Sea of ​​Thieves - youtube.com/watch?v=KmaGxprTUfI&t=1s . К сожалению, им не так много было сказано об инверсии управления в Unreal Engine, однако я написал проект, который демонстрирует, как он работает: github.com/jimmyt1988/UE-Testing
Jimmyt1988

Ответы:

45

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

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

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

Kylotan
источник
5
+1 - я согласен, что сообщество Java, кажется, сильно усложнило то, что кажется очень простой идеей.
Крис Хоу
4
Идея не обязательно заключается в том, что вам нужно поменять различные реализации в производственном процессе, а в том, что вам может потребоваться внедрить специальные реализации для облегчения автоматического тестирования. В этом отношении DI / IoC, безусловно, может быть полезен в играх, но вы хотите, чтобы он был разумно ограничен. Вам не нужно больше, чем несколько разовых разрешений службы на высоком уровне - вы не хотите разрешать службы для каждого маленького объекта в игре и, конечно, не для каждого кадра или чего-либо подобного.
Майк Штробель
2
@ Томас Дюфур: концепция теста никогда ничего не сможет доказать. Это просто точка данных. Вы можете пройти тестовый прогон 100000 раз, не доказав, что он пройдет тестовый прогон 100001. Подтверждение получено в результате логического анализа предмета. Я бы сказал, что эти контейнеры IoC и тому подобное облегчают тестирование за счет усложнения проверки правильности, и я считаю, что это плохо.
Kylotan
13
@ Килотан: концепция теста никогда не пытается ничего доказать . Он пытается продемонстрировать, что поведение с данными входами соответствует ожидаемому. Чем больше будет показано, что поведение правильное, тем больше у вас уверенности в том, что общая функция верна, но никогда не бывает на 100%. Утверждение, что минимальный интерфейс доказывает что-либо, смешно.
даш-том-бэнг
5
@ Алекс Шярер: Боюсь, я никогда не смогу согласиться с тем, что необходимость использования формализованной системы контейнеров IoC и, как правило, создание дополнительных аргументов конструктора или классов фабрики для облегчения тестирования, в любом случае делает код лучше разложенным и, конечно, не делая его слабее в сочетании. Фактически, это в значительной степени попирает инкапсуляцию, ключевой аспект ООП. Вместо того, чтобы полагаться на TDD для управления своим дизайном и надеяться на хороший результат, люди могут просто следовать принципам SOLID.
Kylotan
17

Паттерн стратегии , состав , внедрение зависимостей - все это очень тесно связано.

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

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

Вот статья Мика Уэста, который рассказывает о том, как он ввел этот тип системы в серию игр Tony Hawk от Neversoft.

Развивайте свою иерархию

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

Дэвид Янг
источник
2
+1, Evolve Your Hierarchy - это отличная ссылка, о которой я полностью забыл. Слайды Скотта Биласа тоже хороши - правильная ссылка для них ( scottbilas.com/files/2002/gdc_san_jose/game_objects_slides.pdf ). Есть еще один набор связанных слайдов, но я забыл, где ...
Леандер
Также статья в Games Gems 5 (я думаю) Бьярне Рене.
Крис Хоу
13

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

class Game {
    void Load() {
        this.Sprite.Load(); // loads resource for drawing later
    }
}

class Sprite {
    void Load() {
        FileReader reader = new FileReader("path/to/resource.gif");
        // load image from file
    }
}

Из приведенного выше ясно, что Sprite.Loadзависит от FileReader. Когда вы хотите проверить метод, вам нужно следующее:

  1. Файловая система на месте
  2. Тестовый файл для загрузки из файловой системы
  3. Возможность вызвать общие ошибки файловой системы

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

Цель IoC состоит в том, чтобы отделить использование поведения от его конструкции. Обратите внимание, как это отличается от шаблона стратегии. С помощью шаблона «Стратегия» цель состоит в том, чтобы инкапсулировать многократно используемый фрагмент поведения, чтобы вы могли легко расширить его в будущем; ему нечего сказать о том, как строятся стратегии.

Если бы мы переписали Sprite.Loadописанный выше метод, мы бы, скорее всего, получили :

class Sprite {
    void Load(IReader reader) {
        // load image through reader
    }
}

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

Обратите внимание, что я переписал две вещи. Я создал интерфейс, IReaderкоторый инкапсулировал некоторое поведение - то есть реализовал шаблон Стратегии. Кроме того, я перенес ответственность за создание правильного читателя в другой класс.

Может быть, нам не нужно новое имя шаблона для описания выше. Это выглядит как сочетание шаблонов Strategy и Factory (для контейнеров IoC). При этом я не уверен, на каком основании люди возражают против этого шаблона, поскольку ясно, что он решает реальную проблему, и, конечно, для меня не очевидно, как это связано с Java.

Алекс Шеарер
источник
2
Алекс: никто не возражает против передачи уже созданных объектов, чтобы использовать пользовательские функции там, где это необходимо. Все это делают, как в вашем примере. Проблема в том, что люди используют целые фреймворки, которые существуют исключительно для обработки этого материала, и религиозно кодируют каждый аспект функциональности, чтобы зависеть от этой функциональности. Сначала они добавляют IReader в качестве параметра в конструкцию Sprite, а затем в конечном итоге нуждаются в специальных объектах SpriteWithFileReaderFactory, чтобы облегчить это и т. Д. Это отношение происходит из Java и таких вещей, как контейнеры Spring IoC и т. Д.
Kylotan
2
Но мы не говорим о контейнерах IoC - по крайней мере, я не вижу этого в оригинальном посте. Мы просто говорим о самом шаблоне. Ваш аргумент, как я могу сказать, таков: «Поскольку некоторые инструменты в дикой природе плохи, мы не должны использовать базовые концепции». Это поражает меня, когда я выкидываю ребенка с водой из ванны. Достижение правильного баланса между простотой, выполнением задач и ремонтопригодностью / тестируемостью - сложная проблема, которая, вероятно, лучше всего решается для каждого проекта отдельно.
Алекс Шарер
Я подозреваю, что многие против IoC, потому что это означает:
phtrivier
4

Я бы сказал, что это один из многих инструментов, и он иногда используется. Как сказал tenpn, любой метод, который вводит vtables (и, как правило, любую дополнительную косвенность), может иметь снижение производительности, но это то, о чем нам следует беспокоиться только для низкоуровневого кода. Для структурного кода высокого уровня это на самом деле не проблема, и IoC может иметь положительные преимущества. Что угодно, чтобы уменьшить зависимости между классами и сделать ваш код более гибким.

Крис Хоу
источник
2
Если быть точным, я бы беспокоился о штрафах vtable для любого кода, который многократно вызывается фреймом. Это может быть или не быть низкого уровня.
Tenpn
4

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

Инверсия контроля, с другой стороны, является полезной идеей, которая существует уже давно, и очень полезна, когда все сделано правильно. (Не способ внедрения Java / Dependency Injection.) На самом деле, вы, вероятно, делаете это постоянно, если работаете на нормальном языке программирования. Когда я впервые прочитал об этом "МОК", о котором все жужжали, я был совершенно ошеломлен. Инверсия управления - это не что иное, как передача указателя функции (или указателя метода) на подпрограмму или объект, чтобы помочь настроить его поведение.

Эта идея существует с 1950-х годов. Он находится в стандартной библиотеке C (приходит на ум qsort) и везде в Delphi и .NET (обработчики / делегаты событий). Он позволяет старому коду вызывать новый код без необходимости перекомпиляции старого кода, и он все время используется в игровых движках.

Мейсон Уилер
источник
2
Я также был из "так это то, что люди взволнованы?" когда я читаю о DI, но факт заключается в том, что большинство программистов игр могут узнать об этом и о том, как оно будет применяться к работе, которую мы делаем.
даш-том-бэнг
2

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

Однако следует сказать, что оба метода в C ++ могут вводить виртуальные таблицы там, где их раньше не было, что приводит к соответствующему снижению производительности.

tenpn
источник
1

Я не профессиональный разработчик игр, и только однажды пытался реализовать IoC на C ++, так что это просто предположение.

Однако я подозреваю, что разработчики игр с подозрением относятся к IoC, потому что это означает:

1 / проектирование множества интерфейсов и множества небольших классов

2 / почти каждый вызов функции в последнее время связан

Теперь это может быть совпадением, но оба они, как правило, немного сложнее и / или проблематичны для производительности в C ++ (который широко используется в игре, не так ли?), Чем в Java, где IoC стал широко распространенным (вероятно, потому что это было относительно легко сделать, помогать людям проектировать более разумные иерархии объектов, и потому что некоторые полагали, что это может превратить написание приложения на Java в написание приложения на XML, но это еще одна дискуссия: P)

Я заинтересован в комментариях, если это не имеет никакого смысла вообще;)

phtrivier
источник