Внедрение зависимостей с помощью Ninject, MVC 3 и с использованием шаблона сервисного локатора - PullRequest
13 голосов
/ 05 января 2011

Что-то, что меня беспокоило, так как я прочитал ответ на другой вопрос stackoverflow (точный вопрос теперь ускользает от меня), когда пользователь сказал что-то вроде " Если вы вызываете локатор служб, вы делаете этонеправильно."

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

Однако во многих местах я использовал Service Locator для разрешения зависимостей в моем проекте.Один из лучших примеров взят из моих реализаций ModelBinder.

Пример типичного связывателя модели.

public class FileModelBinder : IModelBinder {
    public object BindModel(ControllerContext controllerContext,
                            ModelBindingContext bindingContext) {
        ValueProviderResult value = bindingContext.ValueProvider.GetValue("id");

        IDataContext db = Services.Current.GetService<IDataContext>();
        return db.Files.SingleOrDefault(i => i.Id == id.AttemptedValue);
    }
}

не реальная реализация - просто быстрый пример

Поскольку реализация ModelBinder требует нового экземпляра, когда Binder запрашивается first , невозможно использовать Dependency Injection для конструктора для этой конкретной реализации.

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

Еще одна проблема, с которой я недавно столкнулся (о которой я написал здесь вопрос), заключалась в том, что все мои контроллеры требовали экземпляр IDataContext, который я использовал DIfor - но один метод действия требовал другого экземпляра IDataContext.К счастью, на помощь пришла Ninject с именованной зависимостью.Тем не менее, это было похоже на клочок, а не на реальное решение.

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

То, как я сейчас его понимаю, - и это тоже может быть неправильно - это то, что, по крайней мере в MVC, ControllerFactory ищет конструктордля контроллера и вызывает сам сервисный локатор, чтобы получить необходимые зависимости, а затем передает их. Однако я могу понять, что не все классы и что не имеют фабрики для их создания.Поэтому мне кажется, что какой-то шаблон Service Locator является приемлемым ... но ...

  1. Когда это неприемлемо?
  2. Каким шаблоном мне следует выглядетькогда мне следует переосмыслить то, как я использую шаблон локатора служб?
  3. Моя реализация ModelBinder неверна?Если да, что мне нужно научиться, чтобы это исправить?
  4. В другом вопросе в соответствии с этим одним пользователем Марк Симан рекомендовал Абстрактную Фабрику - Как это связано?

Полагаю, это все - я не могу придумать ни одного другого вопроса, чтобы помочь моему пониманию, но любая дополнительная информация очень ценится.

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

Я не ищу код, чтобы исправить мою примерную реализацию -Я ищу учиться, ищу объяснение, чтобы исправить мое ошибочное понимание.

Я бы хотел, чтобы stackoverflow.com имел возможность сохранять черновики вопросов.Я также надеюсь, что тот, кто ответит на этот вопрос, получит соответствующую репутацию за ответ на этот вопрос, так как я думаю, что прошу много.Заранее спасибо.

1 Ответ

14 голосов
/ 05 января 2011

Примите во внимание следующее:

public class MyClass
{
  IMyInterface _myInterface;
  IMyOtherInterface _myOtherInterface;

  public MyClass(IMyInterface myInterface, IMyOtherInterface myOtherInterface)
  {
    // Foo

    _myInterface = myInterface;
    _myOtherInterface = myOtherInterface;
  }
}

С этим дизайном я могу выразить требования к зависимости для моего типа.Сам тип не отвечает за знание того, как создать экземпляр какой-либо из зависимостей, он передается ему (внедряется) любым используемым механизмом разрешения [обычно это контейнер IoC].Принимая во внимание:

public class MyClass
{
  IMyInterface _myInterface;
  IMyOtherInterface _myOtherInterface;

  public MyClass()
  {
    // Bar

    _myInterface = ServiceLocator.Resolve<IMyInterface>();
    _myOtherInterface = ServiceLocator.Resolve<IMyOtherInterface>();
  }
}

Наш класс теперь зависит от создания специальных экземпляров, но через делегирование в локатор службы.В этом смысле Service Location может рассматриваться как anti-pattern , потому что вы не выставляете зависимости, но позволяете проблемам, которые могут быть обнаружены во время компиляции, пузыриться во время выполнения.(Хорошее чтение - здесь ).Вы скрываете сложности.

Выбор между тем или иным действительно зависит от того, над чем стоит ваше здание, и от того, какие услуги он предоставляет.Обычно, если вы создаете приложение с нуля, я бы все время выбирал DI.Это улучшает ремонтопригодность, повышает модульность и значительно упрощает типы тестирования.Но, взяв в качестве примера ASP.NET MVC3, вы могли бы легко внедрить SL в качестве встроенного в проект.

Вы всегда можете выбрать комбинированный проект, в котором вы можете использовать IoC / DI с SL, очень похоже на использование Локатор общих служб .Ваши составные части могут быть подключены через DI, но выставлены через SL.Вы даже можете добавить композицию в микс и использовать что-то вроде Managed Extensibility Framework (которая сама поддерживает DI, но также может быть подключена к другим контейнерам IoC или локаторам служб).Это большой выбор дизайна, обычно я рекомендую использовать IoC / DI там, где это возможно.

Ваш конкретный дизайн, я бы не сказал, неверен.В этом случае ваш код не отвечает за создание экземпляра самого механизма связывания модели, это зависит от платформы, поэтому вы не можете контролировать это , но использование вами локатора службы, вероятно, может быть легко изменено наполучить доступ к контейнеру IoC. Но действие вызова resol для контейнера IoC ... не считаете ли вы, что расположение службы?

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

...