Подтвердить что ты не робот

Как избежать антивирусной программы Locator?

Я пытаюсь удалить Service Locator из абстрактного базового класса, но я не уверен, что заменить его. Вот псевдо-пример того, что у меня есть:

public abstract class MyController : Controller
{
    protected IKernel kernel;
    public MyController(IKernel kernel) { this.kernel = kernel); }

    protected void DoActions(Type[] types)
    {

        MySpecialResolver resolver = new MySpecialResolver(kernel);
        foreach(var type in types)
        {
            IMyServiceInterface instance = resolver.Get(type);
            instance.DoAction();
        }
    }
}

Проблема заключается в том, что instanciator производного класса не знает, какие привязки должны иметь ядро, чтобы сохранить MySpecialResolver от выброса исключения.

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

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

Update

Здесь есть две проблемы: одна - контейнер IoC передается контроллеру, действуя как локатор сервисов. Это легко удалить - вы можете перемещать местоположение вверх или вниз по стеку вызовов, используя всевозможные методы.

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

4b9b3361

Ответ 1

Возможно, вам нужно просто удалить ядро, типы и MySpecialResolver и позволить подклассам вызвать DoActions с экземплярами IMyServiceInterface, которые им нужны в качестве аргумента напрямую. И пусть подклассы решают, как они попадают в эти экземпляры - они должны знать лучше (или в случае, если они не знают, кто именно тот, кто когда-либо решает, какие экземпляры IMyServiceInterface нужны)

Ответ 2

Я согласен с @chrisichris и @Mark Seemann.

Отключите ядро ​​от контроллера. Я бы немного изменил состав вашего резольвера, чтобы ваш контроллер мог удалить зависимость от контейнера IoC и разрешить распознавателю быть единственным элементом, который беспокоится о контейнере IoC.

Тогда я бы разрешил преобразовать передатчик в конструктор контроллера. Это позволит вашему контроллеру быть гораздо более проверяемым.

Например:

public interface IMyServiceResolver
{
    List<IMyServiceInterface> Resolve(Type[] types);
}

public class NinjectMyServiceResolver : IMyServiceResolver
{
    private IKernal container = null;

    public NinjectMyServiceResolver(IKernal container)
    {
        this.container = container;
    }

    public List<IMyServiceInterface> Resolve(Type[] types)
    {
        List<IMyServiceInterface> services = new List<IMyServiceInterface>();

        foreach(var type in types)
        {
            IMyServiceInterface instance = container.Get(type);
            services.Add(instance);
        }

        return services;
    }
}

public abstract class MyController : Controller
{
    private IMyServiceResolver resolver = null;

    public MyController(IMyServiceResolver resolver) 
    { 
        this.resolver = resolver;
    }

    protected void DoActions(Type[] types)
    {
        var services = resolver.Resolve(types);

        foreach(var service in services)
        {
            service.DoAction();
        }
    }
}

Теперь ваш контроллер не связан с конкретным контейнером IoC. Кроме того, ваш контроллер гораздо более проверен, так как вы можете издеваться над преобразователями и вообще не использовать контейнер IoC для своих тестов.

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

public abstract class MyController : Controller
{
    private static IMyServiceResolver resolver = null;

    public static InitializeResolver(IMyServiceResolver resolver)
    {
        MyController.resolver = resolver;
    }

    public MyController() 
    { 
        // Now we support a default constructor
        // since maybe someone else is instantiating this type
        // that we don't control.
    }

    protected void DoActions(Type[] types)
    {
        var services = resolver.Resolve(types);

        foreach(var service in services)
        {
            service.DoAction();
        }
    }
}

Затем вы вызываете это при запуске вашего приложения для инициализации преобразователя:

MyController.InitializeResolver(new NinjectMyServiceResolver(kernal));

Мы сделали это для обработки элементов, созданных в XAML, которые нуждаются в зависимостях, но мы хотели удалить запросы службы Locator как запросы.

Прошу прощения за любые синтаксические ошибки:)

Я пишу серию сообщений в блоге по теме рефакторинга приложения MVVM с вызовами Locator Service в моделях просмотра, которые могут показаться вам интересными. Часть 2 скоро появится:)

http://kellabyte.com/2011/07/24/refactoring-to-improve-maintainability-and-blendability-using-ioc-part-1-view-models/

Ответ 3

Мне хотелось бы получить немного больше информации, прежде чем публиковать этот ответ, но Келли поставила меня на место.:) Расскажи мне, чтобы я поместил свой код, где мой рот, так сказать.

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

Итак, вот как я удалю местоположение службы...

Создать командный интерфейс

Прежде всего, я бы создал командный интерфейс для конкретной реализации. В этом случае типы, отправленные с помощью метода DoActions, генерируются из атрибутов, поэтому я бы создал IAttributeCommand. Я добавляю метод Matches к команде, чтобы объявить команду для использования определенными типами.

public interface IAttributeCommand
{
    bool Matches(Type type);
    void Execute();
}

Добавить реализации команд

Чтобы реализовать интерфейс, я передаю конкретные зависимости, которые мне нужны для выполнения моей команды (которая будет разрешена моим контейнером). Я добавляю предикат к методу "Матчи" и определяю свое поведение Execute.

public class MyTypeAttributeCommand : IAttributeCommand
{
    MyDependency dependency;
            SomeOtherDependency otherDependency;

    public MyTypeAttributeCommand (MyDependency dependency, ISomeOtherDependency otherDependency)
    {
        this.dependency = dependency;
                    this.otherDependency = otherDependency
    }

    public bool Matches(Type type)
    {
        return type==typeof(MyType)
    }
    public void Execute()
    {
        // do action using dependency/dependencies
    }
}

Команды регистрации с контейнером

В StructureMap (используйте ваш любимый контейнер), я бы зарегистрировал массив следующим образом:

Scan(s=>
       {
                s.AssembliesFromApplicationBaseDirectory();
                s.AddAllTypesOf<IAttributeCommand>();
                s.WithDefaultConventions();
       } 

Команды выбора и выполнения на основе типа

Наконец, в базовом классе я определяю массив IAttributeCommand в моих аргументах конструктора, которые должны быть введены контейнером IOC. Когда производный тип передается в массиве types, я буду выполнять правильную команду на основе предиката.

public abstract class MyController : Controller
{
    protected IAttributeCommand[] commands;

    public MyController(IAttributeCommand[] commands) { this.commands = commands); }

    protected void DoActions(Type[] types)
    {
        foreach(var type in types)
        {
            var command = commands.FirstOrDefault(x=>x.Matches(type));
            if (command==null) continue;

            command.Execute();
        }
    }
}

Если несколько команд могут обрабатывать один тип, вы можете изменить реализацию: commands.Where(x=>x.Matches(type)).ToList().ForEach(Execute);

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