2016-10-07 4 views
0

Я видел два отличных видеороликов (this и this) о введении зависимостей, законе о демерете и глобальных состояниях (синглтоны считаются глобальными).C++ Инъекция зависимостей + Закон Деметры + регистратор/утверждение

Я думаю, что у меня есть основная идея, но у меня уже есть некоторые одноэлементные классы в моей библиотеке. Однако, если мне нужен тестовый и «хорошо продуманный» или «менее связанный» код, я должен использовать DI и LoD. Это, конечно, означает, что синглтоны (как шаблон дизайна) являются злыми, потому что вызывающий не выполняет сейчас, а любая зависимость от глобальной вещи плоха, по крайней мере, с точки зрения тестирования.

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

Давайте будем более конкретными. У меня есть раздел Math в моей библиотеке, где у меня есть класс Vector2. Он должен иметь возможность «throw assert», когда неверные данные вводятся для одной из его функций. Или должен быть способен зарегистрировать его как ошибку. Или оба. До этого времени я просто использовал Singleton<Logger>, поэтому я смог получить к нему доступ повсюду.

Но я согласен, что эти вещи не должны использоваться, и DI решает эти проблемы. Например. что, если регистратор еще не инициализирован? Что делать, если я хочу фиктивный логгер для тестов? И так далее ... Что вы рекомендуете для этих случаев (например, классы Logger и Assert)?

Также LoD говорит, что я не должен использовать аксессоры для объектов (например, getObjectA()->getObjectB()->doSomething()). Вместо этого передайте их как параметр функции/constructor. Все в порядке, что все проще тестировать (и отлаживать), но может быть больно пропустить эти функции.

Рассмотрим пример двигателя Unity. У GameObject есть метод для получения компонента из этого объекта. Например. если я хочу, чтобы вручную преобразовать свой объект у меня нет выбора для вызова «объект добытчика», что-то вроде этого:

this.GetComponent<Transform>().SetPosition(...); 

Это против Лода, не так ли?

ответ

0

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

Использовать dependency inversion (не только для инъекций).

Что вы рекомендуете для этих случаев (например, классы Logger и Assert)?

DI требует, чтобы вы изменили свои API-интерфейсы, чтобы вы могли вводить вещи, где они используются.Для того, чтобы избежать ситуаций, когда вы должны добавить множество дополнительных параметров (один для регистратора, один для реализации утверждает, или глобальные параметры конфигурации и так далее), объединяют их вместе:

  • создать класс конфигурации во время выполнения
  • добавлять сервисы к нему (служба регистрации, служба проверки, служба конфигурации и т. д.)
  • передать конфигурацию времени выполнения;

Также LoD говорит, что я не должен использовать аксессоров для объектов (например, getObjectA() -> getObjectB() -> йоЗотеЬЫпд()). Вместо этого передайте их как параметр функции/constructor.

Есть несколько проблем, с этим типом цепочки вызовов:

  • поощряет повторение (если у вас есть getObjectA()->getObjectB()-> много раз в вашем коде, то есть уже проблема обслуживания)

  • - это плохая замена для неполного дизайна. LoD говорит, что, если вам нужно doSomething() начиная от экземпляра ObjectA, то Objecta должен иметь метод DoSomething:

    void ObjectA::doSomething(ObjectA& a) 
    { 
        getObjectB()->doSomething(); 
    } 
    

    (или аналогичный).

    Это добавляет естественный момент для расширения того, как «doSomething делается, начиная с экземпляра ObjectA», что хорошо для обслуживания.

  • Он налагает на весь клиентский код, который должен быть doSomething, тот факт, что ему нужно знать об интерфейсе ObjectB. Это звучит мало, но проблема широко распространена, и когда применяется в качестве политики разработки, она плохо соприкасается (если у объекта ObjectA есть не только ObjectB, но также ObjectC и ObjectD, этого может быть достаточно, чтобы заставить вас тратить много время, просто поддерживающее отношения зависимостей).

this.GetComponent<Transform>().SetPosition(...);

Это против Лода, не так ли?

Да. Код можно разделить следующим образом:

void SetPosition(Transform& t) { t.SetPosition(); } 

код клиента:

SetPosition(this.GetComponent<Transform>()); 

Таким образом, чтобы установить позиции в клиентском коде, вы больше не заботиться о интерфейсе Transform. Вы также не заботитесь о реализации void SetPosition(Transform& t), что есть API под названием GetComponent.

реализация Альтернативные LoD для примера выше:

void YourObject::SetTransformPositions() 
{ 
    GetComponent<Transformation>.SetPosition(); 
} 

... где тип this является YourObject*.

+0

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

Смежные вопросы