最后,ApplicationFactory单件类(Singleton)会通过CreateApplication方法创建IApplication的实例,并将已创建的实例返回给调用者,同时保持对已创建实例的引用,以便在框架中任何地方都能够通过ApplicationFactory单件来获得当前执行的IApplication实例。
private static readonly ApplicationFactory instance = new ApplicationFactory();
private IApplication currentApplication;
private static readonly object lockObj = new object();
private ApplicationFactory() { }
public static ApplicationFactory Instance {
get { return instance; }
}
public static IApplication CreateApplication(IConfigurationSource source) {
lock(lockObj) {
if (instance.currentApplication == null) {
lock(lockObj) {
instance.currentApplication = new Application(source);
}
}
return instance.currentApplication;
}
}
public IApplication CurrentApplication {
get { return currentApplication; }
}
}
以上是框架中对多样化配置方式支持的实现部分。在框架的其它部分,则可以使用下面的方式获得所需的配置信息:
// 应用程序或Windows Forms应用程序的Main函数,或者Web应用程序的Global.asax
// 中执行这部分代码:
IConfigurationSource configFileConfigSource = new ConfigFileConfigurationSource(); // 使用web/app.config配置文件
IApplication application = ApplicationFactory.CreateApplication(configFileConfigSource);
// TODO: 在application上执行其它操作
// 在框架中,使用下面的方式访问配置数据:
FrameworkConfigSection configSection = ApplicationFactory.Instance.CurrentApplication.ConfigSource.Config;
// TODO: 使用configSection获取配置数据
当然,我们完全可以不依赖于app/web.config配置文件,而直接使用上面定义的RegularConfigurationSource,以编写代码的方式向框架提供配置数据。这种方式不仅能解决上面提到的框架测试问题,而且还能为开发人员提供强类型和智能感知的支持。比如:
regularSource.SetApplication(typeof(Application).AssemblyQualifiedName);
regularSource.AddObjectContainer(typeof(UnityContainer).AssemblyQualifiedName);
IApplication application = ApplicationFactory.CreateApplication(regularSource);
面向领域特定语言的配置开发模式
通过上面的分析不难得知,要实现多样化的配置方式,无论是基于配置文件的,还是采用其它的实现方式,都离不开配置节(Configuration Section)以及配置数据结构的开发和维护。对于小型的应用程序框架,通常可以使用手工的方式来编写这些配置代码;但对于中大型应用程序框架,为了提供强大的开发能力,配置节和配置数据结构往往比较复杂,此时使用手工方式来维护配置代码就显得费时费力了。为了能够有效地维护这些配置代码,我们可以采用某些领域特定语言(Domain-Specific Language, DSL)来开发配置节和数据结构,然后通过各种代码产生手段实现自动化代码生成。于是,在开发应用程序框架的过程中,当需要变更配置节或配置数据结构时,开发人员就无需去面对繁杂的代码,只需修改领域特定语言即可,剩下的工作都可直接交给自动化代码生成引擎去处理。在设计和开发框架的配置系统时,使用领域特定语言的好处不仅仅在于自动化代码生成,它还能为如下两种应用场景提供支持:
•在产生代码的同时,可以产生配置数据结构的XML Schema,以便在使用Visual Studio编辑配置文件的时候使用智能感知(IntelliSense)技术
•在生成的代码中加入组件模型(Component Model)相关的CLR特性,这将大大简化配置文件编辑器的开发工作

开源社区中有一款配置节设计工具,基本上可以满足上面两条需求,该工具的官方地址是:http://csd.codeplex.com。它能够同时支持 Visual Studio 2005、2008和2010等多个版本。对Visual Studio 2010的支持是以扩展包的方式实现的。在我的个人博客中,有篇文章对该工具进行了简要的介绍,文章地址是:http://www.cnblogs.com/daxnet/archive/2011/09/16/2178377.html,有兴趣的读者可以阅读参考。
领域特定语言在应用程序框架配置系统中的另一个应用就是Fluent Interface。根据《DSLs in Boo Domain Specific Languages in .NET》一书中的介绍,领域特定语言基本上可以分为四大类:External DSL、Graphical DSL、Internal/Embedded DSL以及Fluent Interface。结合上面所述设计,在IConfigurationSource接口上应用Fluent Interface可以为应用程序框架的使用者带来更直观的编程体验。现在,让我们对上面的IConfigurationSource进行扩展,看看Fluent Interface为编程带来的便捷。我们采用.NET 3.0所提供的扩展方法(Extension Methods)来实现这一效果。
{
public static IConfigurationSource SetApplication(this IConfigurationSource source,
string provider) {
source.Config.Application = new ApplicationElement { Provider = provider };
return source;
}
public static IConfigurationSource AddObjectContainer(this IConfigurationSource source,
string provider) {
source.Config.ObjectContainers.Add(new ObjectContainerElement { Provider = provider });
return source;
}
}
// 在使用的时候,就可以:
IConfigurationSource configSource = new RegularConfigurationSource();
configSource
.SetApplication(typeof(Application).AssemblyQualifiedName)
.AddObjectContainer(typeof(UnityContainer).AssemblyQualifiedName);
对于领域特定语言本身的相关知识,在此不作过多讨论,我会在其它的文章中进行详细介绍。
总结
本章节首先以一个单体测试工具为例,论证了为应用程序框架提供多样化的配置方式的必要性,然后针对该需求给出了一种可行的设计方案,同时以伪代码的方式实现了这种设计。在章节的最后,我们还讨论了应用程序框架中配置系统设计与开发的非常好的实践,即使用领域特定语言来维护配置节和配置数据结构,并在使用框架的过程中,利用领域特定语言来获得开发上的便捷。开发应用程序框架,同时也需要为之设计一套合理的、完整的配置数据结构和配置方式,而本章节则对此给出了一种解决方案。在实际中,开发人员完全可以不必按此解决方案来实现框架的配置系统,但它作为配置系统设计的非常好的实践,为开发人员带来了一定的参考价值。