У меня есть следующий дизайн в DDD

  • Опубликовать Объединить с

    • Тело: HTML сообщения
  • Объект Баннер с

    • Html: HTML баннера

Сущность Banner принадлежит агрегату Post, поэтому я хочу создать метод BodyWithBanners в агрегате Post.

Целью этого метода будет поиск в HTML-коде Post.Body и вставка HTML-кода Banner.

Все идет нормально.

Однако у меня есть намерение повторно использовать эту функциональность в абстрактном виде: «Вставьте некоторый HTML в другой HTML». Поэтому я создаю для этого другой класс: BannerReplacer.

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

  • Просто создайте экземпляр внутри метода Post.BodyWithBanners (нарушение внедрения зависимостей).
  • Передача BannerReplacer в конструктор агрегата Post (это может стать кошмаром для создания экземпляров Post)
  • Передача BannerReplacer в метод BodyWithBanners (что означает, что клиент, использующий Post, должен обрабатывать BannerReplacer)

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

0
Manjar 26 Апр 2020 в 12:48

1 ответ

Лучший ответ

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

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

Если Banner является сущностью в смысле domain-driven-design, то это, вероятно, что-то похожее на конечный автомат в памяти. У него есть структура данных, которой он управляет, и некоторые функции для изменения этой структуры данных или ответов на интересные вопросы об этой структуре данных, но у него нет проблем с вводом-выводом, базой данных, сетью и т. д.

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

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

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

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

class Banner {
    void doTheThing(arg, bannerReplacer) {
        /* do the bannerReplacer thing */
    }
}

Обратите внимание, что эта сигнатура вообще не зависит от времени жизни элемента bannerReplacer. В частности, BannerReplacer может иметь более длительный срок службы, чем Banner, или более короткий. Нас заботит только то, что время жизни больше, чем у метода doTheThing.

class Banner {
    void doTheThing(arg) {
        this.doTheThing(arg, new BannerReplacer())
    }

    // ...
}

Здесь вызывающей стороне вообще не нужно знать о BannerReplacer; мы будем каждый раз использовать новую копию реализации по умолчанию. Вызывающий абонент может заботиться о том, какая реализация используется.

class Banner {
    bannerReplacer = new BannerReplacer()

    void doTheThing(arg) {
        this.doTheThing(arg, this.bannerReplacer)
    }

    // ...
}

Та же идея, что и раньше; мы просто используем экземпляр BannerReplacer с более длительным сроком службы.

class Banner {
    Banner() {
        this(new BannerReplacer())
    }

    Banner(bannerReplacer) {
        this.bannerReplacer = bannerReplacer;
    }

    void doTheThing(arg) {
        this.doTheThing(arg, this.bannerReplacer)
    }

    // ...
}

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

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

1
VoiceOfUnreason 26 Апр 2020 в 17:27