具有不同构造函数参数的装饰器

时间:2014-03-13 20:52:26

标签: c# dependency-injection castle-windsor decorator

使用Castle Windsor,我想创建一个记录整数的类。但是我想和其他课程一起装饰几次。如果所涉及的所有混凝土都具有可以解决的依赖关系,我可以看到它是如何工作的,但这不是这里的情况。请考虑以下代码:

public interface IRecorder
{
    void Add(int value);
}

public class NotifyingRecorder : IRecorder
{
    readonly IRecorder decoratedRecorder;

    public NotifyingRecorder(IRecorder decoratedRecorder)
    {
        this.decoratedRecorder = decoratedRecorder;
    }

    public void Add(int value)
    {
        decoratedRecorder.Add(value);
        System.Console.WriteLine("Added " + value);
    }
}

public class ModelUpdatingRecorder : IRecorder
{
    int seed;

    public ModelUpdatingRecorder(int seed)
    {
        this.seed = seed;
    }

    public void Add(int value)
    {
        seed += value;
    }
}

并注册:

container.Register(Component.For<IRecorder>().ImplementedBy<NotifyingRecorder>());
container.Register(Component.For<IRecorder>().ImplementedBy<ModelUpdatingRecorder>());

解决IRecorder将永远不会在此处工作,因为ModelUpdatingRecorder具有非可选的依赖关系。我不能使用静态依赖,因为在编译时不知道seed

有没有办法在运行时指定seed参数并使装饰仍然有效?

此代码示例是我的方案的简化,但想法是一样的。我有装饰器,最低的装饰器依赖于要提供给它的特定值/实例。

2 个答案:

答案 0 :(得分:0)

您可以将种子包装在界面(ISeedHolder?)中,并以单身生活方式注册。然后使用ModelUpdatingRecorder中的接口而不是raw int。除非您的种子可能需要并行化,否则应该允许您在构建ModelUpdatingRecorder时设置种子并解决它

public interface ISeedHolder
{
    int Seed {get;set;}
}

public class ModelUpdatingRecorder : IRecorder
{
    int seed;

    public ModelUpdatingRecorder(ISeedHolder seedHolder)
    {
        this.seed = seedHolder.Seed;
    }

这个解决方案能满足您的需求吗?

答案 1 :(得分:0)

我找到了一个我认为应该这样做的解决方案。在温莎的内部,DefaultDependencyResolver有一个方法,用于解析名为RebuildContextForParameter的子依赖关系(例如上面IRecorder的修饰实例)。它调用它来创建在解析依赖项(即构造函数的参数)时使用的新上下文。方法是:

/// <summary>This method rebuild the context for the parameter type. Naive implementation.</summary>
protected virtual CreationContext RebuildContextForParameter(CreationContext current, Type parameterType)
{
    if (parameterType.ContainsGenericParameters)
    {
        return current;
    }

    return new CreationContext(parameterType, current, false);
}

false构造函数中的CreationContext参数为propagateInlineDependencies,如果为true,则会复制current上下文AdditionalArguments,从而传递子依赖的参数。

要将此false翻转为true,请创建一个派生自DefaultDependencyResolver的新类:

public class DefaultDependencyResolverInheritContext : DefaultDependencyResolver
{
    protected override CreationContext RebuildContextForParameter(CreationContext current, Type parameterType)
    {
        if (parameterType.ContainsGenericParameters)
        {
            return current;
        }

        return new CreationContext(parameterType, current, true);
    }
}

然后在创建Windsor容器时使用它:

var kernel = new DefaultKernel(
                 new DefaultDependencyResolverInheritContext(), 
                 new NotSupportedProxyFactory());
var container = new WindsorContainer(kernel, new DefaultComponentInstaller());

使用NotSupportedProxyFactoryDefaultComponentInstaller的无参数构造函数时,DefaultKernelWindsorContainer是默认设置。

完成后,上面的代码将在使用工厂创建IRecorder时起作用,即:

// during type registration/bootstrapping
container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<IRecorder>().ImplementedBy<NotifyingRecorder>());
container.Register(Component.For<IRecorder>().ImplementedBy<ModelUpdatingRecorder>());
container.Register(Component.For<IRecorderFactory>().AsFactory());

IRecorderFactory的位置:

public interface IRecorderFactory
{
    IRecorder Create(int seed);
}

然后这将按预期工作:

IRecorderFactory recorderFactory = container.Resolve<IRecorderFactory>();
IRecorder recorder = recorderFactory.Create(20);
recorder.Add(6);

希望能帮到别人!