重构笔记---MEF框架(上)

摘要:
本文旨在简要分析和比较MAF和MEF,并详细列出MEF设计和扩展的细节,以便读者在实践中操作。至于为什么使用MEF,读者可以根据自己的项目分析是否有必要使用。MAF和MEFMAF是Microsoft在Framework3.5 3.5中集成的框架,用于解决插件编程问题。它们的优点是严谨但过于死板,发展速度缓慢;MEF是框架4.0中新添加的。其潜在目的是取代MAF的复杂性,加快开发,适应大多数工作场景,并增强易用性。
概述

这篇文章的目的是简要分析对比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,在广义上将就是一种约束,几个“部件”依赖于同一个约定来互相配合、协同,这是人类社会互相协作的精神产物。这种协同思想在软件领域也是同样适用的,包括面向服务、面向接口设计、插件化设计、代码隔离等思想。

重构笔记---MEF框架(上)第1张

代码分析

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

重构笔记---MEF框架(上)第2张重构笔记---MEF框架(上)第3张
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目录中的接口方法。

重构笔记---MEF框架(上)第4张重构笔记---MEF框架(上)第5张
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契约方法、算法:

重构笔记---MEF框架(上)第6张重构笔记---MEF框架(上)第7张
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是系统自动导入的当前加载的插件方法集合。

重构笔记---MEF框架(上)第8张重构笔记---MEF框架(上)第9张
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项目配置

重构笔记---MEF框架(上)第10张

  2. Practise_MEF.Contract项目配置

重构笔记---MEF框架(上)第11张

  3.Practise_MEF.Core项目配置

重构笔记---MEF框架(上)第12张

  4.Practise_MEF.Plugin.CalculateEx项目配置

重构笔记---MEF框架(上)第13张

重构笔记---MEF框架(上)第14张

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

重构笔记---MEF框架(上)第15张

同样输出的目录为….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

免责声明:文章转载自《重构笔记---MEF框架(上)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Redis学习-进阶下(四)Ambari管理的大数据集群主节点内存扩容操作步骤说明下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

MEF 基础简介 一

前言 小编菜鸟级别的程序员最近感慨颇多,经历了三五春秋深知程序路途遥远而我沧海一粟看不到的尽头到不了的终点何处是我停留的驿站。说了段废话下面进入正题吧! 什么是MEF? MEF:全称Managed Extensibility Framework(托管可扩展框架)。单从名字我们不难发现MEF是专门致力于解决扩展性问题的框架。 MSDN:MEF(Managed...

phpstorm重构代码形式让阅读更简单

重构涵盖了一系列不同的技术,包括移动,提取,复制,删除和重命名。 这些功能涵盖了你对代码持续进行地更改。重构是对现有代码的优化和提炼,提高了代码的可读性、可维护性,甚至是提升了代码的执行效率。phpstorm提供了重构的所有功能,可以方便的使用快捷键或菜单对代码进行重构操作。下面是phpstorm代码重构功能介绍: 在文件或选中的代码上点击鼠标右键,弹出框...

VS2010 报表教程--玩转机房重构

机房重构的时候再遇报表,和第一次做的时候不同点是用的VS 中自带的报表,而且用VS中自带的报表控件不仅简单操作,而且方便。只需要三个步骤就可以将它搞定。 1、添加数据集 2、添加表 3、添加报表的控件 Why? 添加数据集的目的是为了和SqlServer连接,要想使用数据库中的表,就要将和数据库进行连接,使用SQL语句选择相应的表。 添加表的目的是为了...

重构的技巧

原文:http://www.cocoachina.com/industry/20140816/9397.html 我想一条童子军的军规:“始终保持露营地比你发现它的时候还要干净”。如果你在地上发现了一点脏东西,不管是谁弄的,都清理掉它。要为了下一拨来露营的人改善环境。(实际上,那条规矩的早期版本,出自Robert Stephenson Smyth Bden...

Eclipse 中的重构功能

Eclipse 中的重构功能 Eclipse 中的重构功能使其成为了一个现代的 Java 集成开发环境 (IDE),而不再是一个普通的文本编辑器。使用重构,您可以轻松更改您的代码,而不必担心对别处造成破坏。有了重构,您可以只关注于所编写代码的功能, 而不必分心去考虑代码的外观如何,因为之后您可以使用重构工具来快捷地将代码变成整洁而高度模块化的代码。本文将...

实战MEF(5):导出元数据

如何理解元数 我们可以把元数据理解为随类型一起导出的附加信息。有时候我们会考虑,把元数据随类型一并导出,增加一些说明,使得我们在导入的时候,可以多一些筛选条件。 默认的类型导出带有元数据吗 上面的内容我说得比较简洁,也许您不是很理解,不要紧,在编程里面,很多东西我们都是写了代码后才理解的。所以,我的理论功底比较差,最不擅长的就是长篇大论,还是从代码中看吧。...