zoukankan      html  css  js  c++  java
  • MEF核心笔记

    MEF核心笔记(5)是模式还是设计

     

    最近事情很多,有烦恼,有悲伤,不过,一切想通后,感觉其实也没什么。毕竟,这是每个人都要经历了,那么恭喜自己,就要当爸爸了,一个程序员爸爸。

    所以,好久没写博客了,今天,我们继续来说MEF,这也是MEF的最后一篇博文,这次的主要内容有:

    同事的设计

    最近同事在做关于WCF的一个项目,而我主要负责WCF通讯的部分,所以无意间看到了他的设计。该同事出生Java,话说Java中的设计模式要比C#起步早很多,但,如果只是为了模式而去设计,那么就失去了模式的意义。

    该项目的业务流程大概可以归结于下图:

    image

    业务逻辑很简单,有客户端发送一个命令给服务器端,服务器端根据不同的命令返回不同的结果。这其中的通讯已经通过WCF实现了,所以,现在主要要设计的便是这命令的解析。

    同事的设计代码如下:

    using System;
    
    public abstract class CommandBase {
    
        public static string Execute(string command) {
    
            CommandBase cmd = null;
            switch (command) {
                case "cmd1":
                    cmd = new CommandA();
                    break;
                case "cmd2":
                    cmd = new CommandB();
                    break;
                default:
                    break;
            }
    
            if (cmd != null) return cmd.Execute();
            return string.Empty;
        }
    
    
        protected virtual string Execute() {
    
            return string.Empty;
        }
    
    }
    
    
    public class CommandA : CommandBase {
        protected override string Execute() {
            return "CommandA";
        }
    }
    
    public class CommandB : CommandBase {
        protected override string Execute() {
            return "CommandB";
        }
    }

    看上去蛮好,有多态,有继承,并且类的耦合度不高。是蛮好的,但是我觉得不适合,有点为了模式而设计的嫌疑。具体有什么问题?那么我们继续就这个设计,来做进一步的讨论。

    针对同事设计随想

    首先,我们看看它的类关系图:

    image

    由于CommandA和CommandB继承至CommandBase,所以,它们与CommandBase是强耦合关系,并且CommandBase中的静态方法引用了CommandA、CommandB,所以,CommandBase对CommandA、CommandB又有依赖关系。

    如此一来,我们发现整个系统中,只有CommandA和CommandB是松耦合的,但是,这是我们所想要的么?其实,恰恰不是,我们期望的是对外提供一个统一的Commad执行接口,并且内部能够安全方便的添加新的Command支持。如果按照这样的设计,难免会出现以下问题:

    • 由于系统中的Commad很多,导致太多的CommandBase子类,引发子类膨胀性增长。
    • CommandBase中的Switch语句会越来越长,与子类的个数相对应。

    解决上述问题,我们可以通过改良一些设计,并引入MEF来解决。

    最后的设计

    其实,解决上述的两个问题也并不怎么困难,针对第一个问题,我们需要优化下程序的结构,而第二个问题,我们就需要引入MEF了。

    首先,我们要理清一下类的职责,CommandBase的设计总让人感觉怪异,特别是它的静态方法。所以我们这里改变一下设计,将Command抽象成一个接口,然后由一个实体类来管理Command的调用,基本结构如下:

    image

    如此一来,我们解除了继承导致的依赖,并且,所有的实现都是依赖于抽象的。但是,我们似乎解决了所有问题,又似乎什么问题也没有解决。有了上面这样的程序结构,我们解决子类膨胀增长的问题,就很简单了,主要是接口的设计:

    public interface ICommand {
        bool CanHandle(string command);
        string Handle(string command);
    }
    

    通过这样的接口设计,我们可以把一组相关的Command放在一个类中实现,只要CanHandle返回true,即代表我们可以处理这个Command。例如,我们将先前同事设计的CommandA和CommandB合并为一个类:

    public class CommandAB : ICommand {
        public bool CanHandle(string command) {
            return command == "cmd1" || command == "cmd2";
        }
    
        public string Handle(string command) {
            if (command == "cmd1")
                return "CommandA";
            else
                return "CommandB";
        }
    }
    

    下面最重要的,是我们的CommandHandler要如何实现,其实也相当的简单:

    public sealed class CommandHandler {
        //单例创建
        private static readonly CommandHandler _instance = new CommandHandler();
        public static CommandHandler Instance { get { return _instance; } }
    
        public IList<ICommand> HandleCommandList { get; private set; }
    
        private CommandHandler() {
            this.HandleCommandList = new List<ICommand>();
        }
    
        public string Handle(string command) {
            foreach (var cmd in this.HandleCommandList) {
                if (cmd.CanHandle(command))
                    return cmd.Handle(command);
            }
            return string.Empty;
        }
    }
    

    仔细看看上面的代码,其实很简单。为了统一管理ICommand,我们将它实现成了单例模式,现在我们可以这样来使用:

    CommandHandler.Instance.HandleCommandList.Add(new CommandAB());
    
    Console.WriteLine(CommandHandler.Instance.Handle("cmd1"));
    Console.WriteLine(CommandHandler.Instance.Handle("cmd2"));
    

    能到这一步,已经很不错了,但是,我们不喜欢手动的Add,所以,现在是MEF最佳的使用时机了(最后完善的代码):

    public interface ICommand {
        bool CanHandle(string command);
        string Handle(string command);
    }
    
    //单例创建
    [Export, PartCreationPolicy(CreationPolicy.Shared)]
    public sealed class CommandHandler {
    
        [ImportMany]
        private IEnumerable<ICommand> _handleCommandList;
    
        private CommandHandler() { }
    
        public string Handle(string command) {
            foreach (var cmd in _handleCommandList) {
                if (cmd.CanHandle(command))
                    return cmd.Handle(command);
            }
            return string.Empty;
        }
    }
    
    [InheritedExport(typeof(ICommand))]
    public abstract class CommandBase : ICommand {
        public abstract bool CanHandle(string command);
        public abstract string Handle(string command);
    }
    
    public class CommandAB : CommandBase {
        public override bool CanHandle(string command) {
            return command == "cmd1" || command == "cmd2";
        }
    
        public override string Handle(string command) {
            if (command == "cmd1")
                return "CommandA";
            else
                return "CommandB";
        }
    }
    

    细看代码,你还是会觉得MEF很有用处的,由此一来,我们解决了所有问题,使用方式如下:

    private static CompositionContainer _container;
    static void Main(string[] args) {
    
        var catalog = new AssemblyCatalog(typeof(Program).Assembly);
        _container = new CompositionContainer(catalog);
    
        var commandHandler = _container.GetExportedValue<CommandHandler>();
        Console.WriteLine(commandHandler.Handle("cmd1"));
        Console.WriteLine(commandHandler.Handle("cmd2"));
    
        while (true) {
            Console.ReadLine();
        }
    }
    

    至此,我们所依赖的,是MEF的容器,后续增加新的Command也会很方便,对外的统一接口更不会改变。通过这一个例子,我们综合性的感受了下MEF的使用,而到这里,MEF的系列也就结束了。

    那么,恭喜你,你应该已经学会使用MEF了。

    作者:MKiller
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
     
    分类: C#
    标签: MEFIoc

    当前标签: MEF

     
    MKiller 2013-04-18 22:12 阅读:298 评论:0
     
    MKiller 2013-04-09 21:29 阅读:119 评论:0
     
    MKiller 2013-04-06 17:25 阅读:114 评论:0
  • 相关阅读:
    [51nod] 1301 集合异或和
    [BZOJ] 1088: [SCOI2005]扫雷Mine
    [LUOGU] P4251 [SCOI2015]小凸玩矩阵
    8.21模拟赛
    [BZOJ] 3163: [Heoi2013]Eden的新背包问题
    [BZOJ] 1001: [BeiJing2006]狼抓兔子
    【NOIP2017提高A组冲刺11.8】好文章
    [BZOJ] 1520: [POI2006]Szk-Schools
    [BZOJ] 1877: [SDOI2009]晨跑
    day23(事务管理)
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3030207.html
Copyright © 2011-2022 走看看