首先什么是Provider模式?Provider是由两个设计模式融合而来的:策略模式+抽象工厂模式。这两个模式具体的介绍我在这里就不多说了,网上一搜一大把。provider模式的作用是为一个API进行定义和实现的分离。这样就通过核心功能的灵活性和易于修改的特点使得API具有灵活性。通俗一点来说就是实现了定义和实现的分离,最终效果就是不需要更改代码即可实现程序不同逻辑的改变。
在BlogEngine中,provider模式被应用于提供不同的数据的持久化。为了保证解压后就能使用默认采用的是xmlProvider。本文研究的重点就是了解这个Provider模式,并知道BlogEngine如何通过provider模式使得不同数据持久化方式之间的灵活切换。
先来看看我实现的Provider模式例子的关系图:
上图中的各个类已经表明了作用。下面我还会在讲解代码的过程中具体解释这些类的。需要说明的是Provider模式不仅仅是可以为系统提供不同的数据持久方式,在业务逻辑中也同样可以提供不同的方式,不一定要拘泥于数据库这块。
先来编写BlogProvider和BlogProviderCollection 。这个类是个抽象类,定义了需要实现的抽象方法以便于子类实现。为了演示的方便,这里我就写了一个方法:
namespace ProviderTest { public abstract class BlogProvider:ProviderBase { public abstract string GetPage(); } public class BlogProviderCollection : ProviderCollection { public new BlogProvider this[string name] { get { return (BlogProvider)base[name]; } } public override void Add(ProviderBase provider) { if (provider == null) throw new ArgumentNullException("provider"); if (!(provider is BlogProvider)) throw new ArgumentException ("Invalid provider type", "provider"); base.Add(provider); } } }
从上面可以看到BlogProvider类继承了ProviderBase类。这个类是.net为我们提供的,里面定义了provider操作的一些“规范”。BlogProviderCollection类用于管理Provider集合,它继承于ProviderCollection类,这个类也是系统提供的,简化了provider操作的步骤。在BlogService中我们会用到这个类来初始化provider集合信息。
下面我再编写DbBlogProvider和XmlBlogProvider类。这两个类继承了BlogProvider类。这是策略模式里面将不同的操作封装起来,使得具体方法的变化不会影响到使用方法的客户。
public class DbBlogProvider :BlogProvider { public override string GetPage() { return "GetPage by db"; } //重写BlogBase里面的Initialize方法 public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) { if (string.IsNullOrEmpty(name)) name = "blogProvider"; if (null == config) throw new ArgumentException("config参数不能为null"); base.Initialize(name, config); } } public class XmlBlogProvider:BlogProvider { public override string GetPage() { return "GetPage by XML"; } public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) { if (string.IsNullOrEmpty(name)) name = "blogProvider"; if (null == config) throw new ArgumentException("config参数不能为null"); base.Initialize(name, config); } }
接着再看BlogProviderConfigureSection这个类,这个类可以看成一个管理配置文件中节点的类。后面在BlogService中获得配置文件的信息就靠它了。在这个类中的属性上面采用了Attribute的方式声明值,具体里面的值可以通过反射得到。
public class BlogProviderConfigureSection:ConfigurationSection { [ConfigurationProperty("providers")] public ProviderSettingsCollection Providers { get { return (ProviderSettingsCollection)base["providers"]; } } [StringValidator(MinLength = 1)] [ConfigurationProperty("defaultProvider", DefaultValue = "XmlBlogProvider")] public string DefaultProvider { get { return (string)base["defaultProvider"]; } set { base["defaultProvider"] = value; } } }
最后就是我们BlogService类了。在BlogEngine中这个类是业务层和表现层的纽带,表现层通过这个方法调用业务层。也可以这样说,这个类实现了业务层的一个统一的访问接口,这样使得代码层次就比较清晰了,可以理解为装饰模式。代码如下:
private BlogProvider _provider; private BlogProviderCollection _providerCollection; public BlogService() { LoadProvider(); } private void LoadProvider() { BlogProviderConfigureSection config = null; if (_provider==null) { //获得blogProvider节点信息 config = (BlogProviderConfigureSection)ConfigurationManager.GetSection("blogProvider"); _providerCollection = new BlogProviderCollection(); //下面这个方法是系统提供的,位于System.web下。如果编写的是form程序则需要自己实现这个providhelper //有兴趣的 可以查看一下他的源码 ProvidersHelper.InstantiateProviders(config.Providers, _providerCollection, typeof(BlogProvider)); //上面那个方法已经加载了providerCollection,这里我们只要DefaultProvider的provider即可 _provider = _providerCollection[config.DefaultProvider]; } } public string GetPage() { return _provider.GetPage(); }
最后不要忘了配置一下web.config,不然程序可不知道用哪个方法。在configsections里面添加:
<section name="blogProvider" type="ProviderTest.BlogProviderConfigureSection,ProviderTest" />
然后在configuration下增加:
<blogProvider defaultProvider="XmlBlogProvider"> <providers> <add name="XmlBlogProvider" type="ProviderTest.XmlBlogProvider"/> <add name="DbBlogProvider" type="ProviderTest.DbBlogProvider"/> </providers> </blogProvider>
最后写一个页面简单的调用一下就可以看到结果了。当你设置改变defaultProvider这个属性值的时候,浏览器简单刷新一下就已经得到了你更改后的结果。
BlogEngine中实现provider的方式基本上和上面讲的差不多。有点区别的地方在于BlogEngine的XmlProvider中,采用了partial关键字定义了XmlProvider类,这样就实现了不同功能的分离,结构更加清晰。