温莎城堡:实现2级(嵌套)工厂的更好方法? [英] Castle Windsor: A better way to implement 2 levels of (nested) factories?

查看:61
本文介绍了温莎城堡:实现2级(嵌套)工厂的更好方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们已经使用了多次模式,通过这种模式,我们可以在单独的Dll中实现处理程序和工厂。我们在运行时配置exe,说明加载了哪些dll,因此该应用程序可以使用哪些处理程序。

We have a pattern we've used several times, whereby we implement handlers and factories in separate Dlls. We configure exe's at runtime saying what dlls are loaded, and therefore what handlers are available to the app.

我们这样做是因为我们为某些客户提供了自定义处理功能允许极大的灵活性,因为我们可以隔离地快速开发新的处理程序,并充满信心地测试和部署它们,甚至我们都不会碰到正在运行的应用程序的任何其他部分。我们还可以通过简单地放入一个替换dll来为处理程序打补丁,我们有严格执行变更管理程序的客户,他们对此表示赞赏。

We do this because we have custom handling for some customers, also it allows great flexibility because we can quickly develop new handlers in isolation, and test and deploy them with confidence that we haven't even touched any other parts of a running application. We can also patch handlers by simply dropping in a single replacement dll, we have customers with strict change management procedures and they adore this.

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

To do this the pattern relies on two levels of factories, specific factories that implement specific handlers, and an overarching factory (which we call a Provider). The Provider chooses which handler factory to use to create a handler.

问题:温莎是否包含可以简化我们流程的内容?

特别是我正在寻找可以省略Handler工厂对象的东西,它感觉就像它应该能够做的一样。

类型化工厂设施 UsingFactory & UsingFactoryMethod 方法,但在这里看不到它们会有什么帮助。

那是我经常找到的城堡温莎文档钝,所以我可能会遗漏一些明显的东西
或者是否有更好的方法来达到我未曾考虑过的相同最终目标。

The question: Does Windsor contain something that would simplify this process for us?
Specifically I'm looking for something that could omit the Handler factory objects, it feels like something it should be able to do.
I've read up on the Typed Factory Facility and the UsingFactory & UsingFactoryMethod methods, but I can't see how they'd be any help here.
That said I often find the Castle Windsor documentation obtuse so I could be missing something obvious Or is there just a better way of getting the same end goal that I haven't considered.

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

Here's some code to illustrate, first message, handler and factory interfaces

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

在第二个DLL中,我们为Type1实现了处理程序和工厂

In a second DLL we implement a handler and factory for 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

In a third Dll we implement a handler and factory for 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服务中,我们实现了提供程序

In a windows service we implement the provider

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);
    }
}

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

The service that actually needs the handlers only uses the Provider

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


推荐答案

在温莎使用类型化工厂确实可以实现您的要求;您可以向Windsor请求链接到消息类型的处理程序,然后使用它,而不是解析提供程序中的所有工厂,然后寻找可以处理消息的工厂。您实际上并不需要二级工厂( IMessageHandlerFactory ),因为处理程序可以告诉它将链接到什么消息。

What you are asking is indeed possible in Windsor with typed factories; instead of resolving all the factories in your provider and then looking for the ones that can process the message, you could ask Windsor for the handler that is linked to the message type and just use it. You don't really need the second level factory (IMessageHandlerFactory), because the handler can tell what message it will link to.

这里是此架构的不错资源(您可能已经阅读了这一个),我将很快对其进行总结。

Here is a nice resource for this architecture (you've probably read this one already) which I'll summarize very quickly.

给出您的界面,您首先需要注册所有处理程序

Given your interfaces, you start by registering all your handlers

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

好,现在让我们告诉温莎我们想要一个工厂,该工厂将返回 IMessageHandler 。令人高兴的是,我们实际上不必为工厂编写任何代码。

Ok, now let's tell Windsor we want a factory that will return a IMessageHandler. What is nice is that we don't actually have to code anything for the factory.

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

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

Now we can start using the factory

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

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

The problem is that since there is no link between our message handlers and the string we pass to the factory, Windsor returns the first registered instance of a IMessageHandler it finds. In order to create this link we can name each message handler after the message type it is supposed to handle.

您可以通过多种方式来完成操作,但是我喜欢创建一个约定,消息处理程序类型告诉它可以处理的消息:

You can do it in a variety of ways, but I like to create a convention where a message handler type tells what messages it can handle:

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

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

Now you need to tell your factory that the message type must be used as the name of the handler you want to resolve. To do that, it is possible to use a class inheriting the DefaulTypedFactoryComponentSelector. We just override the way component names are determined and return the message type we are receiving:

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

现在我们可以将此选择器插入工厂了

Now we can plug this selector in the factory

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

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

Here is the full code to handle any messages:

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);






以下是我要强调的几点:


Here are some points I would like to underline:


  • 您猜到了,您不需要两个工厂:一个就足够了

  • 消息处理程序的命名约定不是一成不变的,您可以决定采用另一种机制来覆盖约定

  • 我不是在谈论释放组件,但是链接包含有关它的一些信息,您应该查看

  • 我没有处理找不到找不到处理程序的情况,但是当Castle无法使用<$解析处理程序时,它会自行抛出c $ c> ComponentNotFoundException

  • 如果处理程序明确说明了它们处理的消息类型,则系统可能会更健壮。例如,将接口更改为 IHandlerOf< T> ,其中 T 是消息类型实现。

  • as you guessed, you don't need the two factories: one is enough
  • the naming convention for the message handlers is not set in stone, and you could decide to have another mechanism in place to override the convention
  • I am not talking about releasing the components, but the links contain some info about it which you should look into
  • I didn't handle the case where no handler can be found, but Castle will throw by itself when it cannot resolve the handler with a ComponentNotFoundException
  • The system could perhaps be more robust if the handlers were explicit about the message types they handle. For example changing the interface to IHandlerOf<T> with T being a message type implementation.

这篇关于温莎城堡:实现2级(嵌套)工厂的更好方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆