【问题标题】:Dynamic/context-sensitive constructor injection动态/上下文敏感的构造函数注入
【发布时间】:2012-07-30 18:08:38
【问题描述】:

我想注入构造函数参数 IActionLogger actionLogger,但希望其他参数 largeBucket、smallBucket 和 amountToRetrieve 是上下文相关的(不确定这是否正确)。

问题:

我是否应该将这些构造函数参数设为自动属性,并将 IActionLogger actionLogger 参数留在构造函数中?

基本上,计算会根据 largeBucket、smallBucket 和 amountToRetrieve 变量而有所不同? 我将这些变量放在构造函数中,因为我需要事先进行一些设置。

public class BucketActionsHandler : IBucketActionsHandler
{
    private List<IAction> _actions = new List<IAction>();
    private Bucket _largeBucket;
    private Bucket _smallBucket;
    private IActionLogger _actionLogger;
    private int _amountToRetrieve;


    public BucketActionsHandler(Bucket largeBucket, Bucket smallBucket, int amountToRetrieve, IActionLogger actionLogger)
    {
        _largeBucket = largeBucket;
        _smallBucket = smallBucket;
        _amountToRetrieve = amountToRetrieve;
        _actionLogger = actionLogger;

        _actions.Add(new LastAction(largeBucket, smallBucket, amountToRetrieve));
        _actions.Add(new EmptySmallerBucketAction(largeBucket, smallBucket, amountToRetrieve));
        _actions.Add(new EmptyLargeBucketAction(largeBucket, smallBucket, amountToRetrieve));
        _actions.Add(new FillLargeBucketAction(largeBucket, smallBucket, amountToRetrieve));
        _actions.Add(new FillSmallBucketAction(largeBucket, smallBucket, amountToRetrieve));
        _actions.Add(new TransferToLargeBucketAction(largeBucket, smallBucket, amountToRetrieve));
        _actions.Add(new TransferToSmallBucketAction(largeBucket, smallBucket, amountToRetrieve));
    }

    private IAction GetNextAction()
    {
        foreach (var action in _actions)
        {
            if (action.SatisfiedCondition())
            {
                return action;
            }
        }
        return null;
    }

    public void CalculateSteps()
    {
        IAction nextAction;
        do
        {
            nextAction = GetNextAction();
            nextAction.Execute();
            if (nextAction == null)
            {
                throw new InvalidOperationException("No valid action available");
            }
        } while(!(nextAction is LastAction));
    }
}

【问题讨论】:

  • 你好,如果你的 BucketActionsHandle 是完全用 IActionLogger 构造的,你可以通过构造函数注入 IActionLogger。其他参数是变量,如果它们在运行时发生变化,你不必注入它们。如果它们是设置一次通过属性注入它们

标签: c# dependency-injection unity-container ninject-2 constructor-injection


【解决方案1】:

我是否应该将这些构造函数参数设为自动属性

不,因为这将允许您在注入或创建此服务后对其进行更改,这是一件坏事,因为服务应该是无状态的,或者至少它们的内部状态更改不应影响应用程序的正确性.当您更改服务的状态时,应用程序代码会强制此服务为临时服务(每次请求时都应注入新实例),而应用程序不应该关心。这会将生命周期服务的控制和决策移出Composition Root(应用程序的启动路径),从而阻碍了可维护性。

改为使用工厂:

public interface IBucketActionsHandlerFactory
{
    IBucketActionsHandler Create(
        Bucket largeBucket,
        Bucket smallBucket,
        int amountToRetrieve);
}

您可以将此工厂注入需要它的服务中,并让该服务提供适当的上下文变量:

public class SomeService
{
    private IBucketActionsHandlerFactory factory;
    private IBucketRepository repository;

    public SomeService(IBucketActionsHandlerFactory factory,
        IBucketRepository repository)
    {
        this.factory = factory;
        this.repository = repository;
    }

    public void Handle(int amountToRetrieve)
    {
        var largeBucket = this.repository.GetById(LargeBucketId);
        var smallBucket = this.repository.GetById(SmallBucketId);

        var handler = this.factory.Create(largeBucket, smallBucket,
            amountToRetrieve);

        handler.CalculateSteps();
    }
}

工厂将控制创建新的IBucketActionsHandler 实现:

public class BucketActionsHandlerFactory
    : IBucketActionsHandlerFactory
{
    private Container container;

    public class BucketActionsHandlerFactory(
        Container container)
    {
        this.container = container;
    }

    public IBucketActionsHandler Create(
        Bucket largeBucket, Bucket smallBucket,
        int amountToRetrieve)
    {
        return new BucketActionsHandler(
            largeBucket, smallBucket, amountToRetrieve,
            this.container.Get<IActionLogger>());
    }
}

您的BucketActionsHandlerFactory 应该是Composition Root 的一部分,在这种情况下,可以将容器/内核注入这个工厂(它是 DI 基础架构的一部分)。

这样,应用程序不知道它获取的处理程序类型,但仍然能够在其当前上下文中获取BucketActionsHandler

或者,您可以将largeBucketsmallBucketamountToRetrieve 变量提供给CalculateSteps 方法。这使您不再需要工厂:

public class BucketActionsContext
{
    public Bucket LargeBucket { get; set; }
    public Bucket SmallBucket { get; set; }
    public int AmountToRetrieve { get; set; }
}

public class BucketActionsHandler : IBucketActionsHandler
{
    private IActionLogger _actionLogger;

    public BucketActionsHandler(IActionLogger actionLogger)
    {
        _actionLogger = actionLogger;
    }

    public void CalculateSteps(
        BucketActionsContext context)
    {
        IAction nextAction;
        do
        {
            nextAction = this.GetNextAction(context);

            if (nextAction == null)
            {
                throw new InvalidOperationException(
                    "No valid action available");
            }

            nextAction.Execute();
        } 
        while(!(nextAction is LastAction));
    }

    private IAction GetNextAction(
        BucketActionsContext context)
    {
        return (
            from action in this.GetActions(context)
            where action.SatisfiedCondition()
            select action)
            .FirstOrDefault();
    }

    private IEnumerable<IAction> GetActions(
        BucketActionsContext context)
    {
        Bucket largeBucket = context.LargeBucket;
        Bucket smallBucket = context.SmallBucket;
        int amountToRetrieve = context.AmountToRetrieve;

        yield return new LastAction(largeBucket, smallBucket, amountToRetrieve);
        yield return new EmptySmallerBucketAction(largeBucket, smallBucket, amountToRetrieve);
        yield return new EmptyLargeBucketAction(largeBucket, smallBucket, amountToRetrieve);
        yield return new FillLargeBucketAction(largeBucket, smallBucket, amountToRetrieve);
        yield return new FillSmallBucketAction(largeBucket, smallBucket, amountToRetrieve);
        yield return new TransferToLargeBucketAction(largeBucket, smallBucket, amountToRetrieve);
        yield return new TransferToSmallBucketAction(largeBucket, smallBucket, amountToRetrieve);    
    }
}

【讨论】:

  • 喜欢使用 LINQ 以及在 CalculateSteps 方法中使用上下文!
【解决方案2】:

您可以手动解析 IActionLogger 并将其手动注入构造函数,同时适当地传递其他参数

您可以在解析BucketActionsHandler 时进行依赖覆盖(这就是它在 Unity 中的调用方式),这将强制它使用传递的值作为注入的依赖项。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-09
    • 1970-01-01
    • 2011-01-01
    相关资源
    最近更新 更多