-1-
基于.NET平台的插件式系统开发
潘昊,何祥军
武汉理工大学计算机学院,湖北武汉(430070)
摘 要:在软件开发过程中,常要面对因需求变化而引起软件更改的情况,不但软件主体需要
变动,同时部署也需要变动。这种代价可能是巨大的。本文针对这种情况,使用插件式的软件
开发方法,成功地避免了因需求变化而造成软件更改的弊端,使系统的可扩展性更强。同时也
说明了插件式(Plug-In) 开发是一种良好的软件开发方法。
关键词:反射,插件,程序集
中图分类号:
1. 引言
随着计算机技术的发展,计算机技术开始应用于国民生产中的各个方面,各行各业的软
件需求,一方面为软件开发提供了机会,另一方面,也为软件开发带来了挑战。某些时候,不
同用户的需求可能只有某一小部分不同;有些时候,用户的需求可能会随着时间的变化而改
变。如何设计一种软件,当用户的需求变化时,只需要替换这些变化的部分,而程序的其他部
分不需要作任何变动;同时,对于某一类特殊的用户,为其提供特殊的功能,而不需要把所有
的功能都提供给他们。“为了设计适应这种变化、且具有健壮性的系统,你必须考虑系统在它
的生命周期内会发生怎么样的变化。”[1] 。
针对这种用户需求可能会变化的情况,本文利用插件式开发方法,通过.NET平台的反射
(Reflection)机制,把对象生成的时机推迟到运行时,根据用户的需求动态加载用户实际需要的
组件,同时,当用户需要新的功能时,只需要把新功能以插件的方式发布给用户,而程序不需
要做任何变动。
2. 实现原理
在.NET平台下使用插件系统,其基本原理是利用.NET平台的反射技术。“反射使用
Assembly 定义和加载程序集,加载在程序集清单中列出的模块,以及从此程序集中查找类型
并创建该类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其
字段和属性”[2]。
实现动态加载程序集方法基本原则有两点:1.封装变化点,把可能变化的部分独立出来,并
封装成程序集文件;2.动态加载程序集,在软件运行时,根据配置文件中的参数,动态调用适
当的程序集,来完成用户的功能需求。当用户需要增加新的功能,也只需要提供新的程序集,
同时更改配置文件即可。
3. 系统设计
下面以从文件导入数据到数据库来说明如何使用这种技术。为了分析此种技术的先进性,
本文通过常规的设计和基于插件式开发方法的对比,来分析使用插件式开发技术的好处。
用例情景
-2-
在这个示例中,客户需要把文件中的数据导入到数据库中,而这种文件的格式可能是多种
多样的,有可能是带分割符的文本文件,也可能按位分割的文本文件,也可能是 Excel文件。
针对这种需求,可以得出图 1:
图 1 文件分析类的结构
通常的处理方式
针对上面的分析,可以设计三种文件分析类,分别为 DelimitFile、FixLengthFile、
ExcelFile,分别对应处理带分割符的文本文件、定长的文本文件及 Excel文件。根据面向对象
“针对接口编程,而不是针对实现编程”[1,4]原则,让这些类都继承 IFileParse接口,接口中有一
个属性以获得文件名;还有一个方法 FileParse分析文件,并保存数据到数据库中。设计如
下:
public interface IFileParse
{
//取得文件名称
public string FileName { set;};
//分析文件,并写入数据库
public static bool FileParse();
}
public class DelimitFile : IFileParse
{
public string fileName = "";
public string FileName
{
set { fileName = value; }
}
public static bool FileParse()
{处理带分割符文本文件,并写入数据库}
}
public class FixLengthFile : IFileParse
{
…
}
public class ExcelFile : IFileParse
{
有分割
符文件
按位分
割文件
Excel
文件
分割符文
件分析器
按分割文
件分析器
Excel分析
器
DB
-3-
…
}
}
由于这里有一系列的文件处理类,并且他们功能相似又相互独立,所以,可以使用工厂模
式[1,3],以方便处理。
public class FileFactory
{
public static IFileParse create(string fileType)
{
switch (fileType)
{
case "DelimitFile" :
return new DelimitFile();
break;
case "FixLengthFile" :
return new FixLengthFile();
break;
case "ExcelFile" :
return new ExcelFile();
break;
}
}
}
这个工厂类的功能是根据传入的参数生成文件处理类实例对象,这个工厂类只有一个静态
的方法 Create,它返回一个文件处理类的实例。当设计完这个工厂类后,当一个文件传入后,
则可以通过传入的文件类型来判断应调用何种分析器,则程序中调用部分的代码如下:
IFileParse parse = (fileType);
();
当用户的只需要处理这三种文件,以上的设计可以很好的完成用户的需求。但是,用户的
需求总是会改变的。假如用户又需要通过此系统导入 XML文件,为了适应这种变化,系统也
需要进行相应的变化,首先增加一个 XML的分析类:
public class XMLFile : IFileParse{…}, 同时,FileFactory中 Create方法中也得增加相应的判断,
以便传入 XML类型时系统能够处理:
case "XMLFile" :
return new XMLFile();
这时,程序仍能很好的完成用户的需求,当用户提供一个 XML时,系统就会调用 XML
分析器,从而完成用户的需求。
但是,必须注意新功能出现时带来的软件方面的变化。XMLFile类的增加是必然的,但
是,为了适应新的功能,FileFactory类也必须改变,这就违返了面向对象的“开放封闭原则
(OCP,类模块应该是可扩展的,但是不可更改)”[4]。同时,必须使程序重新编译,并重新部署
才能完成用户需求。更有一点要清楚的是,当程序很复杂时,为了适应新的需求,可能要使程
序的框架作出很大的改变,如果程序的耦合度很高的话,新功能的加入可能会引出一连串的负
面影响,以至于这种更改的代价可能是无法估量的。
插件式系统开发
-4-
从上面的设计可以看出,当新的需求来临时,当前的软件需要做较大的改动,不但要增加
新的文件分析器,还需要更改程序的其他部分以适应这种需求,同时,这种设计的一个缺点也
显而易见,就是不能应对用户需求的变化,如果用户又要增加一个 CSV文件的导入时,系统
又得重新编译及部署。
使用插件开发方法,可以轻松消除这种缺点,从而使软件的可适性更强,从上面的分析可以
看出,产生系统难以维护的原因在于读入的文件的格式是未知的、不断变化的。如果把这个变
化点提取出来,则系统就不需要做大的变动就能适应用户的需求,利用插件式开发方法,则可
以进行如下设计。
由于要读入的文件的格式是会变化的,可以针对每一种文件格式单独生成一个程序集,这
样做的好处是:1.可动态更换程序所依赖的组件。只要保证新组件的接口不变,组件就能动态
的加载到系统中;2.可动成增加新的功能,根据用户的需求为其提供新的程序集;3.各程序集
相互独立,互不依赖,可以在运行时动态插拨、更换。
如常规设计,把各种已知文件的处理类独立成一个程序集(如 、
等)。在系统运行时,根据传入的文件的类型,动态的调用相应的文件分析器,为了系统运行
时能在运行时调用适当的分析器,可把各分析器的一些配置信息放入到一个配置文件中,在这
里以一个 XML配置文件示例:
<?xml version="" encoding="utf-8" ?>
<root>
<element>
<classname>DelimitFile</classname>
<dllname></dllname>
</element>
……
</root>
配置好文件以后,程序的主体调用可以改为:
//根据用户选择的文件类型,从配置文件中查找处理这种文件类型的程序集。
assemblyFile=GetAssembly(fileTyep);
//根据程序集文件名称,加载程序集
Assembly asm = (assemblyFile);
Type[] types = ();
//搜索程序集中的所有类型
foreach (Type t in types)
{
//查寻继承特定接口的类型
if (("", true))
{
//生成类型的实例
IFileParse caller = (t);
= "...";
();
-5-
}
}
做好如上设计后,当用户再增加新的需求时,比如要增加一个 CSV文件的读取器,则只需
作一个 CSV文件的读取器,放入到特定的目录,然后在配置文件中加入这种文件的配置即
可,而程序的其他部分不需要作任何的改变。
4. 小结
插件式开发方法可以在一定程度上应对用户需求变化时程序也需要作大的修改这种弊端,
必需明确的是,当需求变化时,程序也需要作相应的变化是必然的,只是,插件系统能把程序
需要做的变动降到了一个可以接受的范围,同时会使程序有更强的可扩展性。
插件式系统开发方法,其最主要的应用在于对应软件的变化,而这种变化应是对系统的扩
展,在系统设计之初,设计人员需要分析出系统可能的变化点是什么,这样才能有效的把这些
变化点独立出来,但是,抽取变化点并不是很容易的。当系统设计之后,发现了新的变化点,
可以通过重构的方式逐步把变化的部分提取出来。比如,本系统中导入的目标数据库可能有多
种;同时也需要注意的是,使用反射会降低程序的性能,当程序不会有这种变化时,使用前面
介始的工厂模式就可以了。当系统中具有这种变化时,使用插件式开发方法是一个好的方案。
参考文献
[1] Erich Gamma Richard Helm 等.设计模式(可复用面向对象软件的基础英文版) [M].北京:机械工业出
版社,.
[2] 里克特 著.Microsoft .NET框架程序设计(修订版)[M].李建忠.北京:清华大学出版社,.
[3] 弗里曼(Freeman,E.) 等著.深入浅出设计模式(影印版)[M].南京:东南大学出版社,.
[4] Arthur 著.OOD启思录[M].鲍志云.北京:人民邮电出版社,.
Plug-In Software Development base on .Net Framework
Pan Hao, He XiangJun
WuHan University of Technology Computer College
Abstract
During the period of software development, Change is a common thing when customer wants to have a
new function. Accordingly, software and deployment should be changed too. In this thesis we propose
plug-in software development, use this method, we can avoid the problem mentioned above, and expend
the expansibility of the system. You can learn that plug-in is a good method for developing.
Keywords:Reflection,Plug-In,Assembly