Castle Windsor:实现2级(嵌套)工厂的更好方法?

时间:2014-08-25 16:28:12

标签: castle-windsor factory factory-pattern

我们有多次使用的模式,我们在不同的Dll中实现处理程序和工厂。我们在运行时配置exe,说明加载了哪些dll,以及应用程序可以使用哪些处理程序。

我们之所以这样做是因为我们为一些客户提供定制处理,而且它还具有很大的灵活性,因为我们可以快速开发新的处理程序,并自信地测试和部署它们,我们甚至无法触及任何其他部分。运行的应用程序。我们还可以通过简单地放入一个替换dll来修补处理程序,我们的客户有严格的变更管理程序,他们也很喜欢这个。

要做到这一点,模式依赖于两个级别的工厂,实现特定处理程序的特定工厂,以及一个总体工厂(我们称之为提供商)。 Provider选择使用哪个处理程序工厂来创建处理程序。

问题:Windsor是否包含可以简化此过程的内容?
具体来说,我正在寻找可以省略Handler工厂对象的东西,感觉就像应该能做的事情。
我已经阅读了Typed Factory FacilityUsingFactory& UsingFactoryMethod方法,但我无法看到他们如何在这里提供任何帮助 那说我经常发现Castle Windsor的文档很钝,所以我可能会遗漏一些明显的东西 是否有更好的方法可以达到我无法考虑的最终目标。

这里有一些代码来说明,第一个消息,处理程序和工厂接口

public interface IMessage
{
    string MessageType { get; }
}
public interface IMessageHandler
{
    void Process(IMessage message);
}
public interface IMessageHandlerFactory
{
    bool CanProcessType(string type);
    IMessageHandler Create();
}

在第二个DLL中,我们为Type1

实现了一个处理程序和工厂
public class Type1MessageHandler
    : IMessageHandler
{
    public void Process(IMessage message) { }
}
public class Type1MessageHandlerFactory
    : IMessageHandlerFactory
{
    public bool CanProcessType(string type)
    {
        return type == "Type1";
    }
    public IMessageHandler Create()
    {
        return new Type1MessageHandler();
    }
}

在第三个Dll中,我们为Type2

实现了一个处理程序和工厂
public class Type2MessageHandler
    : IMessageHandler
{
    public void Process(IMessage message) { }
}
public class Type2MessageHandlerFactory
    : IMessageHandlerFactory
{
    public bool CanProcessType(string type)
    {
        return type == "Type2";
    }
    public IMessageHandler Create()
    {
        return new Type2MessageHandler();
    }
}

在Windows服务中,我们实现了提供程序

public interface IMessageHandlerProvider
{
    IMessageHandler Create(string messageType);
}
public class MessageHandlerProvider
    : IMessageHandlerProvider
{
    IEnumerable<IMessageHandlerFactory> factories;
    public MessageHandlerProvider(IWindsorContainer wc)
    {
        factories = wc.ResolveAll<IMessageHandlerFactory>();
    }
    public IMessageHandler Create(string messageType)
    {
        foreach (var factory in factories)
            if (factory.CanProcessType(messageType))
                return factory.Create();
        throw new UnableToFindMessageHandlerFactoryForType(messageType);
    }
}

实际需要处理程序的服务仅使用提供程序

public class MessageService
{
    public MessageService(IMessageHandlerProvider handlerProvider) {}
}

1 个答案:

答案 0 :(得分:5)

在温莎有类型的工厂,你所要求的确是可能的;而不是解析提供程序中的所有工厂,然后查找可以处理消息的工厂,您可以向Windsor询问链接到消息类型的处理程序并使用它。您并不真正需要二级工厂(IMessageHandlerFactory),因为处理程序可以告诉它将链接到哪个消息。

以下是nice resource for this architecture(您可能已经阅读过this one),我会很快总结。

根据您的界面,首先注册所有处理程序

container.Register(Classes.FromAssemblyInThisApplication()
    .BasedOn<IMessageHandler>()
    .WithServiceAllInterfaces());

好的,现在让我们告诉Windsor,我们想要一个能返回IMessageHandler的工厂。有趣的是,我们实际上不必为工厂编码。

container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<IMessageHandlerProvider>().AsFactory());

现在我们可以开始使用工厂了

var provider = container.Resolve<IMessageHandlerProvider>();
var msg = new Type2Message();
var msgHandler = provider.Create(msg.MessageType);

问题在于,由于我们的消息处理程序与传递给工厂的字符串之间没有链接,因此Windsor返回它找到的IMessageHandler的第一个已注册实例。为了创建这个链接,我们可以在它应该处理的消息类型之后命名每个消息处理程序。

您可以通过多种方式执行此操作,但我喜欢创建一个约定,其中消息处理程序类型告诉它可以处理哪些消息:

container.Register(Classes.FromAssemblyInThisApplication()
    .BasedOn<IMessageHandler>()
    .WithServiceAllInterfaces().Configure(c => {
        c.Named(c.Implementation.Name.Replace("MessageHandler", string.Empty));
    }));

现在您需要告诉工厂必须将消息类型用作要解析的处理程序的名称。为此,可以使用继承DefaulTypedFactoryComponentSelector的类。我们只是覆盖组件名称的确定方式,并返回我们收到的消息类型:

public class MessageHandlerSelector : DefaultTypedFactoryComponentSelector
{
    protected override string GetComponentName(MethodInfo method, object[] arguments)
    {
        return arguments[0].ToString();
    }
}

现在我们可以在工厂中插入这个选择器

container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<IMessageHandlerProvider>()
     .AsFactory(c =>c.SelectedWith(new MessageHandlerSelector())));

以下是处理任何消息的完整代码:

var container = new WindsorContainer();
container.Register(Classes.FromAssemblyInThisApplication()
    .BasedOn<IMessageHandler>()
    .WithServiceAllInterfaces().Configure(c => {
        c.Named(c.Implementation.Name.Replace("MessageHandler", string.Empty));
}));

container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<IMessageHandlerProvider>().AsFactory(c =>c.SelectedWith(new MessageHandlerSelector())));

var provider = container.Resolve<IMessageHandlerProvider>();
var msg = new Type2Message();
var msgHandler = provider.Create(msg.MessageType);
msgHandler.Process(msg);

以下是我想强调的一些观点:

  • 如你猜测的那样,你不需要这两个工厂:一个就够了
  • 消息处理程序的命名约定不是一成不变的,您可以决定使用其他机制来覆盖约定
  • 我不是在谈论发布组件,但链接包含一些有关它的信息,您应该查看
  • 我没有处理无法找到处理程序的情况,但是当它无法使用ComponentNotFoundException解析处理程序时,Castle会自行抛出
  • 如果处理程序明确了解它们处理的消息类型,那么系统可能会更强大。例如,将接口更改为IHandlerOf<T>,其中T是消息类型实现。