首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >依赖注入与策略模式

依赖注入与策略模式
EN

Stack Overflow用户
提问于 2014-03-27 23:35:42
回答 4查看 13.7K关注 0票数 26

在这个话题上有大量的讨论,但每个人似乎都错过了一个显而易见的答案。我想帮助审查这个“显而易见的”IOC容器解决方案。各种对话假设在运行时选择策略和使用IOC容器。我会继续这些假设。

我还想补充一种假设,即这不是必须选择的单一战略。相反,我可能需要检索一个对象图,该对象图具有在图的各个节点中找到的几个策略。

我将首先简要概述两种常见的解决方案,然后我将介绍“显而易见”的备选方案,我希望看到IOC容器支持。我将使用统一作为示例语法,尽管我的问题并不是专门针对团结的。

命名绑定

这种方法要求每个新策略都有手动添加的绑定:

代码语言:javascript
复制
Container.RegisterType<IDataAccess, DefaultAccessor>();
Container.RegisterType<IDataAccess, AlphaAccessor>("Alpha");
Container.RegisterType<IDataAccess, BetaAccessor>("Beta");

...and然后明确请求正确的策略:

代码语言:javascript
复制
var strategy = Container.Resolve<IDataAccess>("Alpha");
  • 优点:简单,并得到所有IOC容器的支持
  • 缺点:
    • 通常将调用者绑定到IOC容器,当然还要求调用方了解一些有关策略的信息(比如名称"Alpha")。
    • 每个新策略都必须手动添加到绑定列表中。
    • 这种方法不适合于处理对象图中的多个策略。简而言之,它不符合要求。

抽象工厂

为了说明这种方法,假设以下类:

代码语言:javascript
复制
public class DataAccessFactory{
    public IDataAccess Create(string strategy){
        return //insert appropriate creation logic here.
    }
    public IDataAccess Create(){
        return //Choose strategy through ambient context, such as thread-local-storage.
    }
}
public class Consumer
{
    public Consumer(DataAccessFactory datafactory)
    {
        //variation #1. Not sufficient to meet requirements.
        var myDataStrategy = datafactory.Create("Alpha");
        //variation #2.  This is sufficient for requirements.
        var myDataStrategy = datafactory.Create();
    }
}

接着IOC容器具有以下绑定:

代码语言:javascript
复制
Container.RegisterType<DataAccessFactory>();
  • 优点:
    • IOC容器对消费者隐藏
    • “环境环境”更接近预期的结果,但是.

  • 缺点:
    • 每种策略的建设者可能有不同的需求。但是现在构造函数注入的责任已经从容器转移到抽象工厂。换句话说,每次添加新策略时,都可能需要修改相应的抽象工厂。
    • 大量使用策略意味着大量创建抽象工厂。如果国际奥委会的容器能提供更多的帮助,那就太好了。
    • 如果这是一个多线程应用程序,并且“环境上下文”确实是由线程本地存储提供的,那么当一个对象使用注入的抽象工厂创建它所需的类型时,它可能是在另一个线程上操作,该线程不再能够访问必要的线程本地存储值。

类型切换/动态绑定

这是我想使用的方法,而不是上述两种方法。它涉及提供委托作为IOC容器绑定的一部分。大多数IOC容器都已经具备了这种能力,但是这种特定的方法有一个重要的细微差别。

语法如下所示:

代码语言:javascript
复制
Container.RegisterType(typeof(IDataAccess),
    new InjectionStrategy((c) =>
    {
        //Access ambient context (perhaps thread-local-storage) to determine
        //the type of the strategy...
        Type selectedStrategy = ...;
        return selectedStrategy;
    })
);

注意,InjectionStrategy没有返回IDataAccess的实例。相反,它返回实现IDataAccess的类型描述。接着IOC容器将执行通常的创建和这种类型的“构建”,其中可能包括正在选择的其他策略。

这与标准的类型到委托绑定形成了对比,在统一的情况下,这种绑定的编码方式如下:

代码语言:javascript
复制
Container.RegisterType(typeof(IDataAccess),
    new InjectionFactory((c) =>
    {
        //Access ambient context (perhaps thread-local-storage) to determine
        //the type of the strategy...
        IDataAccess instanceOfSelectedStrategy = ...;
        return instanceOfSelectedStrategy;
    })
);

实际上,上面的内容接近于满足总体需求,但肯定没有达到假设的统一InjectionStrategy

关注第一个示例(它使用了一个假设的统一InjectionStrategy):

  • 优点:
    • 隐藏容器
    • 既不需要制造无穷无尽的抽象工厂,也不需要让消费者摆弄它们。
    • 只要有新的策略,就不需要手动调整IOC容器绑定。
    • 允许容器保留生存期管理控件。
    • 支持纯DI故事,这意味着多线程应用程序可以使用适当的线程本地存储设置在线程上创建整个对象图。

  • 缺点:
    • 由于在创建初始IOC容器绑定时,该策略返回的Type不可用,这意味着第一次返回该类型时性能可能会受到很小的影响。换句话说,容器必须在现场反映类型,以发现它有什么构造函数,以便它知道如何注入它。该类型的所有后续事件都应该是快速的,因为容器可以缓存它第一次发现的结果。这不是一个值得提及的“骗局”,但我正试图充分披露。
    • ???

是否有一个现有的IOC容器可以以这种方式运行?有人有一个统一自定义的注入类来达到这个效果吗?

EN

回答 4

Stack Overflow用户

发布于 2014-03-28 06:33:40

据我所知,这个问题是关于几种候选策略之一的运行时选择或映射。

没有理由依赖DI容器来做到这一点,因为在容器无关的方式中至少有三种方法:

  • 使用元数据角色提示
  • 使用角色接口角色提示
  • 使用部分类型名称角色提示

我个人的偏好是“部分类型名称”角色提示。

票数 21
EN

Stack Overflow用户

发布于 2014-03-28 15:10:09

在过去几年中,我以多种形式达到了这一要求。首先,让我们把我在你的帖子中看到的要点拉出来

假设运行时选择策略和使用IOC容器.添加一个假设,即它不是必须选择的单一策略。更确切地说,我可能需要检索一个有多种策略的对象图.不能将调用者绑定到IOC容器..。每个新策略都不需要手动添加到绑定列表中.如果国际奥委会的容器能提供更多的帮助,那就太好了。

我使用简单喷射器作为我选择的容器已经有一段时间了,这个决定的驱动因素之一是它对泛型有广泛的支持。正是通过这个特性,我们将实现您的需求。

我坚信这段代码应该代表自己,所以我会直接跳进去.

  • 我定义了一个额外的类ContainerResolvedClass<T>来演示Simple找到正确的实现并成功地将它们注入构造函数中。这是类ContainerResolvedClass<T>的唯一原因。(这个类公开了为测试目的而通过result.Handlers注入的处理程序。)

第一个测试要求我们为虚构类Type1获取一个实现。

代码语言:javascript
复制
[Test]
public void CompositeHandlerForType1_Resolves_WithAlphaHandler()
{
    var container = this.ContainerFactory();

    var result = container.GetInstance<ContainerResolvedClass<Type1>>();
    var handlers = result.Handlers.Select(x => x.GetType());

    Assert.That(handlers.Count(), Is.EqualTo(1));
    Assert.That(handlers.Contains(typeof(AlphaHandler<Type1>)), Is.True);
}

第二个测试要求我们为虚构类Type2获取一个实现。

代码语言:javascript
复制
[Test]
public void CompositeHandlerForType2_Resolves_WithAlphaHandler()
{
    var container = this.ContainerFactory();

    var result = container.GetInstance<ContainerResolvedClass<Type2>>();
    var handlers = result.Handlers.Select(x => x.GetType());

    Assert.That(handlers.Count(), Is.EqualTo(1));
    Assert.That(handlers.Contains(typeof(BetaHandler<Type2>)), Is.True);
}

第三个测试要求我们为虚构类Type3获得两个实现。

代码语言:javascript
复制
[Test]
public void CompositeHandlerForType3_Resolves_WithAlphaAndBetaHandlers()
{
    var container = this.ContainerFactory();

    var result = container.GetInstance<ContainerResolvedClass<Type3>>();
    var handlers = result.Handlers.Select(x => x.GetType());

    Assert.That(handlers.Count(), Is.EqualTo(2));
    Assert.That(handlers.Contains(typeof(AlphaHandler<Type3>)), Is.True);
    Assert.That(handlers.Contains(typeof(BetaHandler<Type3>)), Is.True);
}

这些测试似乎满足了您的要求,最好的是解决方案中没有任何容器受到损害。

诀窍是使用参数对象和标记接口的组合。参数对象包含行为的数据(即IHandler),标记接口控制哪些行为作用于哪个参数对象。

下面是标记接口和参数对象--您将注意到,Type3被标记为两个标记接口:

代码语言:javascript
复制
private interface IAlpha { }
private interface IBeta { }

private class Type1 : IAlpha { }
private class Type2 : IBeta { }
private class Type3 : IAlpha, IBeta { }

以下是行为(IHandler<T>):

代码语言:javascript
复制
private interface IHandler<T> { }

private class AlphaHandler<TAlpha> : IHandler<TAlpha> where TAlpha : IAlpha { }
private class BetaHandler<TBeta> : IHandler<TBeta> where TBeta : IBeta { }

这是查找开放泛型的所有实现的唯一方法:

代码语言:javascript
复制
public IEnumerable<Type> GetLoadedOpenGenericImplementations(Type type)
{
    var types =
        from assembly in AppDomain.CurrentDomain.GetAssemblies()
        from t in assembly.GetTypes()
        where !t.IsAbstract
        from i in t.GetInterfaces()
        where i.IsGenericType
        where i.GetGenericTypeDefinition() == type
        select t;

    return types;
}

这是为我们的测试配置容器的代码:

代码语言:javascript
复制
private Container ContainerFactory()
{
    var container = new Container();

    var types = this.GetLoadedOpenGenericImplementations(typeof(IHandler<>));

    container.RegisterAllOpenGeneric(typeof(IHandler<>), types);

    container.RegisterOpenGeneric(
        typeof(ContainerResolvedClass<>),
        typeof(ContainerResolvedClass<>));

    return container;
}

最后,测试类ContainerResolvedClass<>

代码语言:javascript
复制
private class ContainerResolvedClass<T>
{
    public readonly IEnumerable<IHandler<T>> Handlers;

    public ContainerResolvedClass(IEnumerable<IHandler<T>> handlers)
    {
        this.Handlers = handlers;
    }
}

我意识到这篇文章很长,但我希望它能清楚地展示出解决你的问题的可能方法。

票数 1
EN

Stack Overflow用户

发布于 2016-06-17 12:47:54

这是一个迟来的反应,但也许它会帮助其他人。

我有个很简单的方法。我只需创建一个不直接依赖于统一的StrategyResolver。

代码语言:javascript
复制
public class StrategyResolver : IStrategyResolver
{
    private IUnityContainer container;

    public StrategyResolver(IUnityContainer unityContainer)
    {
        this.container = unityContainer;
    }

    public T Resolve<T>(string namedStrategy)
    {
        return this.container.Resolve<T>(namedStrategy);
    }
}

用法:

代码语言:javascript
复制
public class SomeClass: ISomeInterface
{
    private IStrategyResolver strategyResolver;

    public SomeClass(IStrategyResolver stratResolver)
    {
        this.strategyResolver = stratResolver;
    }

    public void Process(SomeDto dto)
    {
        IActionHandler actionHanlder = this.strategyResolver.Resolve<IActionHandler>(dto.SomeProperty);
        actionHanlder.Handle(dto);
    }
}

注册:

代码语言:javascript
复制
container.RegisterType<IActionHandler, ActionOne>("One");
container.RegisterType<IActionHandler, ActionTwo>("Two");
container.RegisterType<IStrategyResolver, StrategyResolver>();
container.RegisterType<ISomeInterface, SomeClass>();

现在,这方面的好处是,我以后在添加新策略时再也不用碰StrategyResolver了。

这很简单。非常干净,我把对团结的依赖性保持在最低限度。只有当我决定改变集装箱技术时,我才会接触到StrategyResolver,这是不太可能发生的。

希望这能有所帮助!

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22701412

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档