在我的代码上运行FxCop,我收到此警告:
Microsoft.Maintainability:'FooBar.ctor与来自9个不同命名空间的99种不同类型相结合.重写或重构方法以减少其类耦合,或考虑将方法移动到与其紧密耦合的其他类型之一.高于40的类耦合表明可维护性差,40到30之间的类耦合表明适度的可维护性,并且低于30的类耦合表明良好的可维护性.
我的类是来自服务器的所有消息的登陆区域.服务器可以向我们发送不同EventArgs类型的消息:
public FooBar() { var messageHandlers = new Dictionary>(); messageHandlers.Add(typeof(YouHaveBeenLoggedOutEventArgs), HandleSignOut); messageHandlers.Add(typeof(TestConnectionEventArgs), HandleConnectionTest); // ... etc for 90 other types }
"HandleSignOut"和"HandleConnectionTest"方法中的代码很少; 他们通常将工作交给另一个班级的职能部门.
如何通过较低的耦合使这个类更好?
让工作的类注册他们感兴趣的事件 ...... 事件代理模式.
class EventBroker { private Dictionary> messageHandlers; void Register (Action subscriber) where T:EventArgs { // may have to combine delegates if more than 1 listener messageHandlers[typeof(T)] = subscriber; } void Send (T e) where T:EventArgs { var d = messageHandlers[typeof(T)]; if (d != null) { d(e); } } }
您也可以使用某种IoC框架(如Spring.NET)来注入字典.这样,如果您获得新的消息类型,则不必重新编译此中央集线器 - 只需更改配置文件即可.
创建一个名为Example的新控制台应用程序,并添加以下内容:
using System; using System.Collections.Generic; using Spring.Context.Support; namespace Example { internal class Program { private static void Main(string[] args) { MessageBroker broker = (MessageBroker) ContextRegistry.GetContext()["messageBroker"]; broker.Dispatch(null, new Type1EventArgs()); broker.Dispatch(null, new Type2EventArgs()); broker.Dispatch(null, new EventArgs()); } } public class MessageBroker { private Dictionaryhandlers; public Dictionary Handlers { get { return handlers; } set { handlers = value; } } public void Dispatch (object sender, T e) where T : EventArgs { object entry; if (Handlers.TryGetValue(e.GetType(), out entry)) { MessageHandler handler = entry as MessageHandler ; if (handler != null) { handler.HandleMessage(sender, e); } else { //I'd log an error here Console.WriteLine("The handler defined for event type '" + e.GetType().Name + "' doesn't implement the correct interface!"); } } else { //I'd log a warning here Console.WriteLine("No handler defined for event type: " + e.GetType().Name); } } } public interface MessageHandler where T : EventArgs { void HandleMessage(object sender, T message); } public class Type1MessageHandler : MessageHandler { public void HandleMessage(object sender, Type1EventArgs args) { Console.WriteLine("Type 1, " + args.ToString()); } } public class Type2MessageHandler : MessageHandler { public void HandleMessage(object sender, Type2EventArgs args) { Console.WriteLine("Type 2, " + args.ToString()); } } public class Type1EventArgs : EventArgs {} public class Type2EventArgs : EventArgs {} }
和app.config文件:
输出:
Type 1, Example.Type1EventArgs Type 2, Example.Type2EventArgs No handler defined for event type: EventArgs
如您所见,MessageBroker
不知道任何处理程序,处理程序不知道MessageBroker
.所有映射都在app.config文件中完成,因此如果您需要处理新的事件类型,可以将其添加到配置文件中.如果其他团队正在定义事件类型和处理程序,这是特别好的 - 他们可以在dll中编译他们的东西,将其放入部署中并简单地添加映射.
Dictionary具有object类型的值,而不是MessageHandler<>
因为实际的处理程序无法转换为MessageHandler
,所以我不得不破解它.我认为解决方案仍然很干净,它可以很好地处理映射错误.请注意,您还需要在此项目中引用Spring.Core.dll.你可以找到图书馆在这里,和文档在这里.在依赖注入一章是有关这一点.另请注意,没有理由需要使用Spring.NET - 这里的重要思想是依赖注入.不知何故,某些事情需要告诉代理将类型a的消息发送到x,并且使用IoC容器进行依赖注入是让代理不了解x的好方法,反之亦然.
与IoC和DI相关的其他一些问题:
依赖注入(DI)和控制反转(IOC)之间的差异
哪个C#/ .net依赖注入框架值得研究?
我应该使用哪种依赖注入工具?