我们有多次使用的模式,我们在不同的Dll中实现处理程序和工厂。我们在运行时配置exe,说明加载了哪些dll,以及应用程序可以使用哪些处理程序。
我们之所以这样做是因为我们为一些客户提供定制处理,而且它还具有很大的灵活性,因为我们可以快速开发新的处理程序,并自信地测试和部署它们,我们甚至无法触及任何其他部分。运行的应用程序。我们还可以通过简单地放入一个替换dll来修补处理程序,我们的客户有严格的变更管理程序,他们也很喜欢这个。
要做到这一点,模式依赖于两个级别的工厂,实现特定处理程序的特定工厂,以及一个总体工厂(我们称之为提供商)。 Provider选择使用哪个处理程序工厂来创建处理程序。
问题:Windsor是否包含可以简化此过程的内容?
具体来说,我正在寻找可以省略Handler工厂对象的东西,感觉就像应该能做的事情。
我已经阅读了Typed Factory Facility和UsingFactory
& 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) {}
}
答案 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
是消息类型实现。