Внедрение зависимостей в проектах с большим количеством классов, которые нужно генерировать на лету другими классами - PullRequest
1 голос
/ 03 апреля 2011

Я разрабатываю своего рода переводчик с языка A на B (да, это как компилятор). Перевод обычно делается из нескольких разных файлов, и каждый из них имеет 3 одинаковых раздела для перевода. Итак, как я это сделал, у меня вроде как это так:

enter image description here

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

Думаю ли я, что это неправильно или это просто так? Мне не разрешено использовать какие-либо рамки DI / IoC в этом проекте, кстати.

Edit:

Боюсь, я не передаю свое сообщение.

В этом конкретном случае, поскольку мой класс Translator должен иметь возможность в любой момент генерировать какой-либо FileTranslator, ему потребуется FileTranslatorFactory. Я знаю, что IoC Container может выполнить подключение за меня, но сам по себе IoC Container не избавит меня от проблемы необходимости кодировать код самого FileTranslatorFactory. Я прав?

Теперь проблема в том, что FileTranslator также должен быть в состоянии генерировать всякий раз, когда ему нужны SectionATranslators, SectionBTranslators и SectionCTranslators (и не думать, что они похожи, потому что их имена - они совершенно разные и не имеют ничего общего с делать друг с другом!). Таким образом, я должен был бы определить фабрики для каждого из них. Поэтому для такой простой системы из 5 классов мне нужно было бы создать 4 (!!!) фабрики.

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

1 Ответ

6 голосов
/ 03 апреля 2011

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

РЕДАКТИРОВАТЬ - с полностью вымышленной структурой, чтобы сохранить это как можно более независимым, но объясняя, как можно исключить все, кроме одного вызова в контейнер во многих сценариях.

Итак, с реализациейиз Translator вроде:

public class Translator
{
    private ITranslator translatorInstance;

    public Translator()
    {
        SomeContainer container = SomeContainer.CreateFromConfig(configFilePath);

        // this is the ONLY point we touch the container
        translatorInstance = container.GetMeA<ITranslator>();
    }

    // implementation
}

Мы можем видеть, что это работает как фабрика и является единственным классом, который должен знать о самом контейнере.Следовательно, реализация одного конкретного разработчика ITranslator может быть такой:

public class FileTranslator : ITranslator
{
    // private fields

    public FileTranslator(  ISectionATranslator sectionAtrans, 
                            ISectionBTranslator sectionBtrans, 
                            ISectionCTranslator sectionCtrans)
    {
        this.sectionAtrans = sectionAtrans;
        // etc
    }

    // implementation
}

Обратите внимание, что FileTranslator ничего не знает о том, от каких конкретных классов на самом деле реализуются интерфейсы, от которых оно зависит, и не нуждается ни в каком виде.завод.На самом деле, контейнер сделает это за вас.Есть несколько способов, которыми контейнеры справляются с этим, один пример - явная конфигурация, например:

<!-- absolutely fictitious configuration file, but similar to many frameworks -->
<ContainerConfig>
    <ObjectResolver interface="ITranslator">
        <ConcreteType type="FileTranslator">
            <ConstructorInjection>
                <Argument ordinal="0" type="SectionATranslator" />
                <Argument ordinal="1" type="SectionBTranslator" />
                <Argument ordinal="2" type="SectionCTranslator" />
            </ConstructorInjection>
        </ConcreteType>
    </ObjectResolver>
</ContainerConfig>

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

Также обратите внимание, что некоторые платформы предоставляют возможность определять эти правила разрешения типов в коде, используя API-интерфейсы в свободном стиле,а некоторые позволяют вам определить несколько возможных способов разрешения определенного типа с помощью некоторого имени (возможно, реализация "Production" по сравнению с реализацией "UnitTest").

Обратите внимание, что я оставил вышеизложенное намеренно расплывчатым, потому что яне хотите говорить, какая платформа является лучшей (и, честно говоря, я думаю, что это зависит от ваших индивидуальных потребностей) - проверьте в других местах StackOverflow для сравнения структур, и, пожалуйста, попробуйте несколько (возможно, вы можете попробовать некоторые, не сказав своему боссу!).Надеемся, однако, что вышеприведенное показывает, почему контейнер IoC может сделать ваш код намного чище, устраняя необходимость в слоях на слоях фабричных классов.

...