摘要:.NET 是 Microsoft XML Web services 平台。MEF是.NET Framework 4.0一个重要的库,Visual Studio 2010 Code Editor的扩展支持也是基于MEF构建的。下面让我们一起来看。

  MEF(Managed Extensibility Framework)是.NET Framework 4.0一个重要的库,Visual Studio 2010 Code Editor的扩展支持也是基于MEF构建的。MEF的目标是简化创建可扩展的应用程序,其核心类是ComposablePart,即具有组合能力的组件,每一个称为ComposablePart(中文可为可组合构件,不过下文一直采用英文来表示,这样比较贴切)的组件可以组合(称为Import)其它组件的功能(其它组件通过声明Export提供功能)并且它也可以通过定义Export将其功能暴露给其它组件。

  ComposablePart通过组件目录(ComposablePartCatalog)来搜索发现需要的功能,组件目录可以是一个物理文件目录、网络存储等。每一个ComposablePart还具备动态组合的能力,在必要的情况下可以重新组合功能。本文将采用自底向上的思路体验一下MEF的设计思想。

  1、无废话MEF

  MEF的核心是可组合组件ComposablePart,它由ComposablePartDefintion来描述和创建。每一个可组合组件通过定义ExportDefintion向其它组件提供功能,通过ImportDefinition引用其它组件的功能,通过Metadata来描述组件自身的信息。在创建一个ComposablePart组件后,通过在组件目录(ComposableCatalog)搜索需要的功能实现组件组合。

  2、典型的MEF组合过程

  (1)创建组件目录(如AssemblyCatalog)

  (2)创建组合容器CompositionContainer,组件容器通过组件目录搜索组件的定义

  (3)创建一个组件

  (4)从组件容器获取其它组件功能的定义,然后执行匹配组合

  示例代码如下:

  1. 1. var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); //创建一个程序集目录,用于从一个程序集获取所有的组件定义
  2. 2. var container = new CompositionContainer(catalog); //创建一个组合容器
  3. 3. var composablePart = new MyComponent();
  4. 4. container.ComposeParts(composablePart); //执行组合,从容器中获取ExportDefinition并创建实例组合在一起
  5. 5. // composablePart组合完成以供使用

  其原理如下图(来自mef.codeplex.com官方网站):

  3 MEF本质组合基元

  组合基元是对提供具有可扩展、可组合能力的组件的本质支持,它处于MEF的最底层,是整个Framework的核心类,由6个类构成,如下图所示(该图来自MEF白皮书,白皮书有点抽象,不过看起来很过瘾,后面附上本人翻译的中文版)。

  组合基元类的描述如下:

  (1)ComposablePart:即可组合组件,是组合基元的核心类。ExportDefinitions表示该组件提供的功能的描述;而ImportDefinitions则是对引用其它组件功能的约束的描述。Metadata是对组件自身的特殊标识,当一个ComposablePart通过Import引用其它组件功能时,元数据可能作为满足引用功能的约束的一个条件。

  (2)ExportDefinition:定义ComposablePart向其它组件提供的功能,这个功能使用一个ContactName和Metadata来描述。ContactName即使用这个功能的契约,Metadata用于进一步描述这个功能。

  (3)ImportDefinition:定义ComposablePart对其它组件提供的功能的引用,即引用了另一个组件的Exports。ImportDefintion使用一个表达式来描述约束,它在Constraint这个属性定义,其类型为Expression>。这个表达式用于对一个ExportDefintion做匹配判定,其匹配方法如下:

  以下是代码片段:

  1. 1. var allExportDefs = // 从ComposablePartCatalog获取所有ExportDefinition
  2. 2. var constraintDelegate= Constraint.Compile(); //编译成匹配函数的代理
  3. 3. var satisfiedExportDefs = allExportDefs .FindAll(constraintDelegate); //使用匹配函数的代理来过滤所有的ExportDefs

  (4)ComposableDefinition:即ComposablePart定义,是ComposablePart的工厂,该类定义了一类ComposablePart引用的功能、暴露的功能及其自身的元数据。引用的功能在ImportDefinitions中描述,暴露的功能通过ExportDefinitions描述。而Metadata则是对组件自身的描述,在MEF中一般用于在一个组件引用(Import)另一个组件功能时,通过对另一个组件的元数据进行匹配,从而来确定是否要组合另一个组件提供的功能。该类是ComposablePart的工厂,提供了CreatePart方法。

  (5)ComposablePartCatalog:可组合组件目录,用于发现组件,这些组件可能来自物理目录、网络存储等。

  4 、如何使用MEF

  在上面,我们描述了MEF的核心组合基元,组合基元听起来很简单,很容易理解,但是想直接使用组合基元来编写一个ComposablePartDefinition却不是那么容易了,在MEF的实现,这些类都是一些抽象类,用于描述整个可扩展框架的模型。我先不想说明白MEF到底是如何来使用组合基元,先看示例好了。

  4.1 定义ComposablePartDefinition

  MEF通过引入一个基于特性的编程模型来简化ComposablePart的定义,如下所示的MessageSender和Processor类均是ComposablePart定义。

  以下是代码片段:

  1. 1. public class MessageSender
  2. 2. {
  3. 3. [Export("MessageSender")]
  4. 4. public void Send(string message)
  5. 5. {
  6. 6. Console.WriteLine(message);
  7. 7. }
  8. 8. }
  9. 9. [Export]
  10. 10. public class Processor
  11. 11. {
  12. 12. [Import("MessageSender")]
  13. 13. public Action MessageSender { get; set; }
  14. 14. public void Send()
  15. 15. {
  16. 16. MessageSender("Processed");
  17. 17. }
  18. 18. }

  4.2、 创建ComposablePart

  以下是代码片段:

  1. 1. var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); //创建一个程序集目录,用于从一个程序集获取所有的组件定义
  2. 2. var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); //创建组件目录
  3. 3. var container = new CompositionContainer(assemblyCatalog); //创建组合容器
  4. 4. var processorPart = new Processor();
  5. 5. container.ComposeParts(processorPart); //执行组合
  6. 6. processorPart.Send();
  7. 7. Console.ReadLine();

  4.3 、基于特性编程模型的本质

  通过4.1和4.2的示例可以发现,MessageSender和Processor这两个类型就是ComposablePartDefintion的实现,在这两个类型,我们通过Export和Import(ImportMany)特性来定义暴露的功能和引用的功能。  CompositionContainer通过这两个类所在的程序集的组件目录来搜索所有的可组合组件定义,然后在执行组合时利用这些定义创建Export对象,根据Import声明的约束契约实现组件的组合。

  在这个编程模型里面,它允许我们:(1)使用传统OOP的类型定义来定义一个ComposablePartDefinition,毋庸置疑,这基本没有引入复杂的概念;(2)使用Export/Import/ImportMany等元数据来声明组合功能,非常的简单且容易理解。

  CompositionContainer将会在后台构建这个Part对应的ComposablePartDefinition以及组件目录其它ComposablePartDefinition,在执行组合时,利用Definition创建实例执行组合。

  5、 MEF vs MAF vs Unity

  在刚学习MEF时,经常会问一个问题,那就是MEF和MAF这样的插件框架、和Unity这样的IoC框架到底有什么区别。MEF与MAF(Managed Addin Framework)最大不同在于:前者关注使用非常简单的方式来支持具有很强灵活性的可扩展支持,后者关注具有物理隔离、安全、多版本支持的插件平台架构;MEF和Unity不同在于:前者强调组合,后者强调依赖注入。

  6、 MEF总结

  MEF有3点让我非常的深刻,首先是组合基元的设计,其次是基于特性的编程模型,最后是MEF的实现方法。

组合基元是可扩展支持的本质,它看起来显得非常的简单,但却有能够支持强大的功能能力并且不失灵活性。大道至简,不过,简的程度确实因人而异,MEF的简实在让人佩服得五体投地。这个Framework也是除了ObjectBuilder之外让我非常喜欢的框架,查看其代码真是让人无比舒畅。

  天人之作啊!这帮人的创新能力太强悍了!

  基于特性的编程模型,允许我们使用类的定义 + 特性声明的方式来定义一个具有组合能力的组件,它使得我们基于MEF编写组件变得非常非常的简单!这也让我再次体会到面向上下文编程方法的魅力~,后面我也会介绍一下我原来做过的一个基于上下文思想设计的FW,和MEF的思路有点类似。

  MEF在实现时,其顶层命名空间是System.ComponentModel.Composition,底下划分了AttributeModel、Diagnostics、Hosting、Primitives、ReflectionModel命名空间。MEF的顶层命名空间定义了我们使用最多的特性,底下命名空间分别用于定义特性模型、诊断支持、MEF宿主、组合基元、反射模型,整体实现非常的清晰简洁!看第一眼我就爱上这玩意了!

  7 、基于特性编程模型的另一个示例

  我原来设计了一个基于特性的智能体编程框架。首先,我来简洁的描述什么是智能体。智能体就是软件代理人,用软件来模拟人类的特性,包括智能性、主动性、社会性、感知性等。从实现角度来看,一个智能体就是一个绑定了线程、消息队列的对象,这个对象用线程来模拟人类大脑,用消息队列来模拟大脑记忆体。当智能体收到一条消息时,其线程会接管来处理。根据上述描述,大家肯定觉得使用OOP开发智能体有点麻烦。OK,那下面来看看我是如何使用上下文实现智能体的。

  7.1 使用特性来声明一个具有感知能力和主动性的人

  以下是代码片段:

  1. 1. [Agent] 
  2. 2. public class SomePerson
  3. 3. {
  4. 4. [Intelligent]
  5. 5. public virtual OpenTheDoor()
  6. 6. {
  7. 7. // 开门,主动性方法
  8. 8. }
  9. 9. [Sensible(Environment.Temperature)]
  10. 10. public virtual OnTemperatureChanged(SensibilityContext context)
  11. 11. {
  12. 12. // 当感知到温度变化的响应,感知性声明
  13. 13. }
  14. 14. }

  7.2 创建智能体

  以下是代码片段: 

  1. 1. var agentContainer = new AgentContainer();
  2. 2. var agent = agentContainer.Build(); //在后台构建一个真正的智能体
  3. 3. agent.OpenTheDoor(); //调用OpenTheDoor方法,这个调用最终会转变成消息发送给真正的智能体由其本身来执行,就像某人让另一人去关门一样,最终将由接收到消息的人去执行关门这个动作。

  AgentFramework具有和MEF类似的设计方法(当然咱们的内功和Microsoft那帮高手没得比了),通过定义类型 + 声明智能体特性来定义智能体,这种方式简单、灵活且可扩展性强!

MEF——.NET中值得体验的精妙设计的更多相关文章

  1. .NET中值得体验的精妙设计

    转自: http://developer.51cto.com/art/201104/255455_all.htm .NET 是 Microsoft XML Web services 平台.MEF是.N ...

  2. APP开发中,如何从UI设计上提升APP用户体验

    设计中有很多细微的东西要注意,就如UI设计中,元素的统一性,图标风格.段落的排版等等,只有能注意这些细节,你的 APP UI 才算合格. 干货君总结了17个提升用户体验的 UI 设计小技巧,也是我们日 ...

  3. 抓到 Netty 一个隐藏很深的内存泄露 Bug | 详解 Recycler 对象池的精妙设计与实现

    欢迎关注公众号:bin的技术小屋,如果大家在看文章的时候发现图片加载不了,可以到公众号查看原文 本系列Netty源码解析文章基于 4.1.56.Final版本 最近在 Review Netty 代码的 ...

  4. C++11 中值得关注的几大变化(网摘)

    C++11 中值得关注的几大变化(详解) 原文出处:[陈皓 coolshell] 源文章来自前C++标准委员会的 Danny Kalev 的 The Biggest Changes in C++11 ...

  5. 使用Bootstrap3和Ladda UI实现的多种按钮“加载中”效果体验

    在线演示 在线演示 大家在开发基于web的网站或者web应用中,常常在AJAX调用的过程中需要提示用户并且展示相关的“加载中”效果,类似的UI设计也非常多,比如,当点击一个按钮后,在它的旁边显示一个 ...

  6. 在APP开发中,如何优雅的设计APP页面

    1.明确页面设计在整个产品设计中的位置 互联网产品设计的流程大致是:产品定位——需求分析——信息架构设计——流程设计——页面框架设计——设计说明——输出设计文档.可以看到页面设计是处于整个流程的后期, ...

  7. Java生鲜电商平台-电商中海量搜索ElasticSearch架构设计实战与源码解析

    Java生鲜电商平台-电商中海量搜索ElasticSearch架构设计实战与源码解析 生鲜电商搜索引擎的特点 众所周知,标准的搜索引擎主要分成三个大的部分,第一步是爬虫系统,第二步是数据分析,第三步才 ...

  8. Spark小课堂Week7 从Spark中一个例子看面向对象设计

    Spark小课堂Week7 从Spark中一个例子看面向对象设计 今天我们讨论了个问题,来设计一个Spark中的常用功能. 功能描述:数据源是一切处理的源头,这次要实现下加载数据源的方法load() ...

  9. 在java中String类为什么要设计成final

    在java中String类为什么要设计成final? - 胖胖的回答 - 知乎 https://www.zhihu.com/question/31345592/answer/114126087

随机推荐

  1. POJ 1696 Space Ant(凸包变形)

    Description The most exciting space discovery occurred at the end of the 20th century. In 1999, scie ...

  2. POJ 1815 Friendship(最大流最小割の字典序割点集)

    Description In modern society, each person has his own friends. Since all the people are very busy, ...

  3. 自测之Lesson4:gdb

    题目:列出gdb过程中常用的命令. 常用命令: 命令 作用 使用示例1 使用示例2 list 列出代码 list 行号 list 函数名 break 设置断点 break 行号 b 行号 run 运行 ...

  4. Java学习个人备忘录之线程间的通信

    线程间通讯多个线程在处理同一资源,但是任务却不同. class Resource { String name; String sex; } //输入 class Input implements Ru ...

  5. Calculation PartⅡ

    GitHub/object-oriented 误删内容--周末修复

  6. Java微笔记(3)

    Java 中的 static 使用之静态变量 Java 中被 static 修饰的成员称为静态成员或类成员. 它属于整个类所有,而不是某个对象所有,即被类的所有对象所共享. 静态成员可以使用类名直接访 ...

  7. iOS中UIButton控件的用法及部分参数解释

    在UI控件中UIButton是极其常用的一类控件,它的类对象创建与大多数UI控件使用实例方法init创建不同,通常使用类方法创建: + (id)buttonWithType:(UIButtonType ...

  8. Oracle导数据到SQL server的方法总结

    通过oracle10g 访问sql server 2008 导数据步骤 最近在项目中遇到要将Oracle数据库的数据导入到SQL server数据库中,解决办法如下: 一.准备工作 配置Oracle ...

  9. 第22天:js改变样式效果

    一.输出语句 1.alert:弹出警示框(用的非常少,用户体验不好)完整写法:window.alert(“执行语句”):window对象,窗口,一般情况可省略alert(123); 2.console ...

  10. 在html在添加cookie和读取cookie

    1.保存cookie var oDate = new Date(); oDate.setDate(oDate.getDate() + );//有效期为30天 document.cookie = &qu ...