zoukankan      html  css  js  c++  java
  • 重构笔记---MEF框架(上)

    概述

    这篇文章的目的是简要分析对比MAF和MEF,并详细举出MEF设计中的细节和扩展上的细节,达到让读者能实际操作的目的。其中,MAF的设计会附上我的代码,其实就是官方的代码我自己手动联系了一遍,但还是很有收获的,不动手光看是不会体会到细节的;MEF是我着重介绍的,当然也是微软推荐的解决方案,所以这部分内容会多一些。

    至于为什么要用MEF(插件框架)读者可针对自己的项目分析是否有必要使用。

    文章中难免有不足和错误,还请大家不吝指出,互相交流。

    MAF和MEF

    MAF是微软集成在Framework3.5中的为解决插件化编程的框架,其优势是严谨但过于死板,开发速度慢;MEF是集成在Framework4.0中新增加的,潜在的目的是替换MAF的繁琐而使开发速度增快并且适合绝大多数的工作场景,增强易用性。

    下面这篇博客的作者已经对此分析的很全面了,有兴趣的请参考:

    http://www.cnblogs.com/techborther/archive/2012/02/06/2339877.html

    接口契约

    无论是MAF和MEF框架均需要一个中间引用集——“契约”,插一句题外话,契约在狭义上来讲就是C#中的接口Interface,在广义上将就是一种约束,几个“部件”依赖于同一个约定来互相配合、协同,这是人类社会互相协作的精神产物。这种协同思想在软件领域也是同样适用的,包括面向服务、面向接口设计、插件化设计、代码隔离等思想。

    代码分析

    首先,定义契约层:定义方法和数据接口,仅仅是声明接口

    namespace Practise_MEF.Contract
    {
        public interface ICalculator
        {
            String Calculate(String input);
        }
    
        public interface IOperation
        {
            int Operate(int left, int right);
        }
    
        public interface IOperationData
        {
            Char Symbol { get; }
        }
    
        public interface IMultiOperation
        {
            int MultiOperate(Practise_MEF.Core.Module.InputParams p);
        }
    }
    View Code

    然后,编写服务端(Host)解析方法:服务端要定义CompositionContainer对象,此对象需要Add类别对象,包括本程序内部定义的契约实现方法(MyCalculate)和外部PlugIns目录中的接口方法。

    namespace Practise_MEF
    {
        public class MyCalculateLoader
        {
            private CompositionContainer _container;
    
            [Import(typeof(ICalculator))]
            public ICalculator calculator;
    
            public MyCalculateLoader()
            {
                //An aggregate catalog that combines multiple catalogs
                var catalog = new AggregateCatalog();
                //Adds all the parts found in the same assembly as the Program class
                catalog.Catalogs.Add(new AssemblyCatalog(typeof(MyCalculate).Assembly)); 
                catalog.Catalogs.Add(new DirectoryCatalog(@"...OutputPlugIns"));
    
                //Create the CompositionContainer with the parts in the catalog
                _container = new CompositionContainer(catalog);
    
                //Fill the imports of this object
                try
                {
                    this._container.ComposeParts(this);
                }
                catch (CompositionException compositionException)
                {
                    Console.WriteLine(compositionException.ToString());
                }
            }
        }
    }
    View Code

    接着,实现MyCalculate契约方法、算法:

    namespace Practise_MEF
    {
        public class MyCalculate
        {
            [Export(typeof(IOperation))]
            [ExportMetadata("Symbol", '+')]
            public class Add : IOperation
            {
                public int Operate(int left, int right)
                {
                    return left + right;
                }
            }
    
            [Export(typeof(IOperation))]
            [ExportMetadata("Symbol", '-')]
            public class Subtract : IOperation
            {
                public int Operate(int left, int right)
                {
                    return left - right;
                }
            }
        }
    }
    View Code

    在然后,在本程序集中处理插件组方法:称为插件组是因为可能会有很多插件方法在系统的Container中,需要根据业务需求去区分应用那个插件或者全部;其中operations是系统自动导入的当前加载的插件方法集合。

    namespace Practise_MEF
    {
        [Export(typeof(ICalculator))]
        public class MyCalculateAdapter : ICalculator
        {
            [ImportMany]
            IEnumerable<Lazy<IOperation, IOperationData>> operations;
    
            public String Calculate(String input)
            {
                int left;
                int right;
                Char operation;
                int fn = FindFirstNonDigit(input); //finds the operator
                if (fn < 0) return "Could not parse command.";
    
                try
                {
                    //separate out the operands
                    left = int.Parse(input.Substring(0, fn));
                    right = int.Parse(input.Substring(fn + 1));
                }
                catch
                {
                    return "Could not parse command.";
                }
    
                operation = input[fn];
    
                foreach (Lazy<IOperation, IOperationData> i in operations)
                {
                    if (i.Metadata.Symbol.Equals(operation))
                        return i.Value.Operate(left, right).ToString();
                }
                return "Operation Not Found!";
            }
    
            private int FindFirstNonDigit(String s)
            {
    
                for (int i = 0; i < s.Length; i++)
                {
                    if (!(Char.IsDigit(s[i]))) return i;
                }
                return -1;
            }
    
        }
    }
    View Code

    最后,在编写扩展的插件方法:

    namespace Practise_MEF.Plugin.CalculateEx
    
    {
    
        /// <summary>
    
        /// Mod
    
        /// </summary>
    
        [System.ComponentModel.Composition.Export(typeof(Practise_MEF.Contract.IOperation))]
    
        [System.ComponentModel.Composition.ExportMetadata("Symbol", '%')]
    
        public class CalculateMod : Practise_MEF.Contract.IOperation
    
        {
    
            public int Operate(int left, int right)
    
            {
    
                return left % right;
    
            }
    
        }
    
    }

    至此,一个简单、完整的插件应用已经完成,可实现动态加载处理2个数字的算法(取余),当软件需动态增加功能时,只序编写xxxxEx.dll,然后Copy到软件的PlugIns目录下即可,软件会动态增加功能。相比而言,MEF的后面隐藏和简化了更多的操作,是用户仅仅按需完成几步操作即可完成软件的插件化,使项目更灵活。

    项目配置

    在某个磁盘上新建一个Output文件夹,并且在Output目录下新建一个PlugIns文件夹,名称要固定,在项目中更改代码可修改名称,这不同于MAF约定过于死板。

    1. Practise_MEF项目配置

      2. Practise_MEF.Contract项目配置

      3.Practise_MEF.Core项目配置

      4.Practise_MEF.Plugin.CalculateEx项目配置

    项目中需要添加项目引用 Practise_MEF.Contract.dll,其属性设置为 Copy Local 为 False 切记。

    同样输出的目录为….OutputPlugIns,这样设置属性后,Practise_MEF.Contract.dll不会一同输出到PlugIns目录中,约束使用Practise_MEF.Plugin.CalculateEx.dll的工程中要保证有Contact.dll。

    示例代码下载

    引用

    MAF和MEF区别:http://www.cnblogs.com/techborther/archive/2012/02/06/2339877.html

    MEF官方解释:http://msdn.microsoft.com/en-us/library/dd460648.aspx

    MEF分析:http://www.360doc.com/content/11/0830/16/7582031_144521508.shtml

  • 相关阅读:
    git保存用户名密码的方式
    解决HP电脑播放视频,暂停后再次播放,前几秒钟会没有声音
    我们本想通过网络了解广袤的世界,而不是陷入推荐的漩涡
    cnblogs回来了
    mysql commit和事务
    服务器重启crontab服务
    linux多个命令行执行
    quick bi 配置即席查询sql 参数
    python 不定长参数
    python 和 mysql 的时间格式化
  • 原文地址:https://www.cnblogs.com/cuiyansong/p/4128684.html
Copyright © 2011-2022 走看看