Преимущество IoC перед моим Factory Singleton - PullRequest
9 голосов
/ 31 января 2012

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

Вот ситуация: я создаю простой веб-сервер на основе HttpListener в службе Windows, который использует модель подключаемого модуля для определения способа обработки запроса на основе запрошенного URL-адреса (как и все остальные, кто спрашивает). о HttpListener). Мой подход к обнаружению плагинов состоит в том, чтобы запрашивать в сконфигурированном каталоге сборки, украшенные HttpModuleAssemblyAttribute. Эти сборки могут содержать 0 или более IHttpModule дочерних элементов, которые, кроме того, украшены HttpModuleAttribute, используемым для указания имени модуля, версии, понятного человеку описания и различной другой информации. Что-то вроде:

[HttpModule(/*Some property values that matter */)]
public class SimpleHttpModule : IHttpModule
{
    public void Execute(HttpListenerContext context)
    {
        /* Do Something Special */
    }
}

Когда обнаруживается HttpModule, я обычно добавляю его к объекту Dictionary<string, Type>, единственная цель которого - отслеживать, о каких модулях мы знаем. Этот словарь обычно встречается в моем разнообразии синглтонов, которые берут на себя персону синглтона в стиле ACE (наследие моих дней в C ++, где я узнал о синглетонах).

Теперь я пытаюсь реализовать нечто подобное, используя (мое понимание) общие концепции IoC. По сути, у меня есть коллекция AppService, где IAppService определяется как:

public interface IAppService : IDisposable
{
    void Initialize();
}

А мой плагин AppService будет выглядеть примерно так:

[AppService("Plugins")]
internal class PluginAppService : IAppService, IDictionary<string, Type>
{
    /* Common IDictionary Implementation consisting of something like: */
    internal Type Item(string modName)
    {
        Type modType;
        if (!this.TryGetValue(modName, out modType)
            return null;

        return modType;
    }

    internal void Initialize()
    {
        // Find internal and external plug-ins and add them to myself
    }

    // IDisposable clean up method that attempts to dispose all known plug-ins
}

Затем во время обслуживания OnStart Я создаю экземпляр AppServices, который известен локально, но передается конструктору всех экземпляров подключаемых модулей:

public class AppServices : IDisposable, IDictionary<string, IAppService>
{
    /* Simple implementation of IDictionary */

    public void Initialization()
    {
        // Find internal IAppService implementations, instantiate them (passing this as a constructor parameter), initialize them and add them to this.

        // Somewhere in there would be something like
        Add(appSvcName, appSvc);
    }
}

Наша когда-то единичная реализация метода становится абстрактной реализацией + конструктором для потомка:

[HttpModule(/*Some property values that matter */)]
public abstract class HttpModule : IHttpModule
{
    protected AppServices appServices = null;
    public HttpModule(AppServices services)
    {
        appServices = services;
    }            

    public abstract void Execute(HttpListenerContext context);
}

[HttpModule(/*Some property values that matter */)]
public class SimpleHttpModule : HttpModule
{
    public SimpleHttpModule(AppServices services) : base(services) { }
    public override void Execute(HttpListenerContext context)
    {
        /* Do Something Special */
    }
}

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

var plugType = appServices["Plugins"][plugName];

вместо:

var plugType = PluginManager.Instance[plugName];

Я упускаю здесь некоторую базовую концепцию IoC, которая упростила бы все это, или есть ли польза для всего этого дополнительного кода? В моем мире синглтоны - это простые существа, которые позволяют коду всей программы получать доступ к необходимой (относительно статичной) информации (в данном случае типов).

Чтобы задать вопросы более четко:

  1. Является ли это действительной реализацией Factory Singleton, переведенной в понятия IoC / DI?
  2. Если это так, где окупаемость / выгода для дополнительного кода и навязывание, казалось бы, более неуклюжего API?

Ответы [ 3 ]

4 голосов
/ 31 января 2012

IoC - это общий термин.Инъекция зависимостей является более предпочтительным термином в наши дни.

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

Во-вторых, внедрение зависимостей создает не только требуемый тип, но и все зависимые типы.Таким образом, если класс A нуждается в классе B, а класс B - в классах C и D, то хорошая структура DI автоматически создаст все зависимости и будет управлять их временем жизни (например, заставляя их жить в течение жизни одного веб-запроса).

Контейнеры DI могут представлять собой общие фабрики, которые могут создавать экземпляры любого типа объекта (при условии, что он правильно настроен и соответствует требованиям инфраструктуры DI).Так что вам не нужно писать собственную фабрику.

Как и в случае любого универсального решения, оно разработано так, чтобы предоставить 90% вариантов использования то, что им нужно.Несомненно, вы можете создавать вручную созданную структуру данных пользовательского связанного списка каждый раз, когда вам нужна коллекция, но 90 =% времени универсальная будет работать просто отлично.То же самое относится к фабрикам DI и Custom.

3 голосов
/ 31 января 2012

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

EDIT

Тот факт, что вы можете достичь той же функциональности с помощью синглетонов, не означает, что его так же легко поддерживать. Используя IoC (по крайней мере, этот стиль с конструкторами), вы явно указываете зависимости, которые имеет объект. Используя синглтоны, эта информация скрыта в классе. Это также затрудняет замену этих зависимостей альтернативными реализациями.

Таким образом, с одиночным PluginManager было бы трудно протестировать ваш HTTP-сервер с помощью фиктивных плагинов, скорее, он ищет их из какого-то места на диске. С версией IoC вы можете обойти альтернативную версию IAppService, которая просто ищет плагины из предварительно заполненного Dictionary.

0 голосов
/ 09 марта 2012

Хотя я все еще не совсем уверен, что IoC / DI лучше в этой ситуации, я определенно увидел выгоду, когда охват проекта охватил.Для таких вещей, как логирование и конфигурируемость, это, безусловно, правильный подход.

Я с нетерпением жду возможности поэкспериментировать с ним в будущих проектах.

...