好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

MEF核心笔记

MEF核心笔记

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

 

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

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

同事的设计 针对同事设计随想 最后的设计

同事的设计

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

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

业务逻辑很简单,有客户端发送一个命令给服务器端,服务器端根据不同的命令返回不同的结果。这其中的通讯已经通过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" ;
    }
}

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

针对同事设计随想

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

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

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

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

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

最后的设计

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

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

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

 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

出处: http://www.cnblogs.com/prinsun/

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

 

分类:  C#

标签:  MEF ,  Ioc

当前标签: MEF

 

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

 

MEF核心笔记(4)细说MEF中的Attribute [下]

 

MEF核心笔记(3)细说MEF中的Attribute [上]

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

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

版权信息

查看更多关于MEF核心笔记的详细内容...

  阅读:44次