技术开发 频道

提供多样化配置 .NET应用框架架构设计

  最后,ApplicationFactory单件类(Singleton)会通过CreateApplication方法创建IApplication的实例,并将已创建的实例返回给调用者,同时保持对已创建实例的引用,以便在框架中任何地方都能够通过ApplicationFactory单件来获得当前执行的IApplication实例。

public class ApplicationFactory {
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; }
}
}

  以上是框架中对多样化配置方式支持的实现部分。在框架的其它部分,则可以使用下面的方式获得所需的配置信息:

// 框架初始化时,可以用以下的方式创建IApplication实例。比如可以在控制台
// 应用程序或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,以编写代码的方式向框架提供配置数据。这种方式不仅能解决上面提到的框架测试问题,而且还能为开发人员提供强类型和智能感知的支持。比如:

IConfigurationSource regularSource = new 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 class FluentInterfaceProvider
{
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);

  对于领域特定语言本身的相关知识,在此不作过多讨论,我会在其它的文章中进行详细介绍。

  总结

  本章节首先以一个单体测试工具为例,论证了为应用程序框架提供多样化的配置方式的必要性,然后针对该需求给出了一种可行的设计方案,同时以伪代码的方式实现了这种设计。在章节的最后,我们还讨论了应用程序框架中配置系统设计与开发的非常好的实践,即使用领域特定语言来维护配置节和配置数据结构,并在使用框架的过程中,利用领域特定语言来获得开发上的便捷。开发应用程序框架,同时也需要为之设计一套合理的、完整的配置数据结构和配置方式,而本章节则对此给出了一种解决方案。在实际中,开发人员完全可以不必按此解决方案来实现框架的配置系统,但它作为配置系统设计的非常好的实践,为开发人员带来了一定的参考价值。

0
相关文章