本篇文章更多是基于这篇文章翻译而来,当中融入了自己对于文章的理解,也算是读书笔记了。希望能让更多的同学了解并掌握这一有用的模式,将它应用在你的程序中。
. why we need it
一直觉得要了解一个东西首先应该了解的就是它产生的原因。只有搞清楚了事物出现的来龙去脉你才能够对现在的情况有一个清晰的认识,这是一个大前提!正如文章中所说,Fixed Choices、Fixed Internal Behaviors以及Fixed Data Schema是我们在使用一些类库的api时所经常遇到的问题。比如“Fixed Choices”,asp.net的session支持一个名为out-of-process的工作模式,意即你可以将自己应用程序的状态数据存储在asp.net的工作进程之外,可选项只有两个要么存储在asp.net专有的state server中,要么存储在sql server中。是的,如你所想,这里便有一个问题,如果我想要使用mysql来存储呢?
再比如“Fixed Internal Behaviors”,仍然是asp.net session的out-of-process,假设现在我们想将存储在进程外sql server的状态数据取回,这个时候asp.net提供的api行为是固定的,我们只能是要么将存储的状态数据一次性全部取回要么一点也不取到。试想下如果存储的数据总共有50mb但是我们需要的仅仅是其中的50kb...you get the picture!
类似的例子还有很多,其实一言以概之就是:
“the inability to change core behavior or functionality of published APIs.”
我们没有一个好的方式去改变那些已经发布的api的既有行为!
解决办法呢,你可能会说没关系我写一个自己的“session state”,这很好但是连带的也会产生很多问题,最明显的就是开发人员需要放弃已经熟悉的那些api转而学习你这一套专有api,这就是成本,效率应此会降低。so...
“What we really want is a way for everyone to get the benefits of well-documented and familiar programming interfaces, such as Session State, but the ability to completely control the internal Business Logic Layer (BLL) and Data Access Layer (DAL) of these APIs”
我们真正想要的就是,在能继续使用自己对于规范良好文档齐全的api的已有经验的同时,拥有对这些api内部逻辑的完整控制。
. the provider design pattern
从本质上来说,provider这个模式是非常简单的。它之所以叫“provider”也正是因为这个provider提供了上层api实际的行为逻辑。比如文章中所提到的,asp.net中的Membership拥有一个叫做ValidateUser()的静态方法,但是Membership类本身并没有包含关于该方法的任何业务逻辑,相反的,当ValidateUser被调用时它只是简单的将调用转移给已经被配置好的provider,由这个provider负责实现ValidateUser方法的具体逻辑。
“It is the responsibility of the provider class to contain the implementation for that method, calling whatever Business Logic Layer (BLL) or Data Access Layer (DAL) is necessary.”
实际上我个人认为,关于provider模式的介绍到这里就可以结束了。因为我们已经很完整的了解到了一个模式的产生原因以及它的详细定义。剩下的只是如何去具体实现这样一个模式而已,能力强的同学几乎已经可以在脑中自己构思然后开始动手实现了(当然可能需要经过好多个版本的迭代才能最终拿出一个像样的provider模式实现来)。由于平台的不同,所受限制的不同(包括语言的,框架的等等)一个模式的最终实现可能会有所区别,但是内在本质一定是一样的,所以下面我们来看看在asp.net中的provider模式吧。
. provider in asp.net
事实上从另外一篇文章我们可以看出,asp.net下的provider模式似乎更是特指了对状态存储服务的provider化。简单说来,asp.net 2.0中提供了很多服务,包括众所周知的membership服务,site maps服务,profile服务以及其它一些服务等等,这些服务的一个共性就是需要将应用程序在运行过程中的一些状态数据进行持久化存储,我们的provider模式在这里刚好能够大显身手。上层服务api与下层状态持久化的媒体并不直接交互,而是通过一个个的provider来进行衔接。如下图所示:
当然我个人认为,provider模式并不仅仅限于解耦上层服务与底层物理存储媒介的交互,只是在asp.net中provider被大规模用于此类应用罢了。
“A provider is a software module that provides a uniform interface between a service and a data source. Providers abstract physical storage media, in much the same way that device drivers abstract physical hardware devices. Because virtually all ASP.NET 2.0 state-management services are provider-based, storing session state or membership state in an Oracle database rather than a Microsoft SQL Server database is as simple as plugging in Oracle session state and membership providers. Code outside the provider layer needn't be modified, and a simple configuration change, accomplished declaratively through Web.config, connects the relevant services to the Oracle providers.”
在asp.net中所有的provider都直接或间接的继承于一个名为ProviderBase的基类(位于System.Configuration.Provider名称空间下)。PrivoderBase上面是具体的ApplicationProviderBase,比如MembershipProviderBase。简单说这个基类是衔接底层Base类和上层更为具体的Provider的一个中间类。再来便是真正具体的provider了,比如SqlServerMemberhipProviderBase或者OracleMembershipProviderBase。用下面这张图能够更清楚的说明:
同时我们还必须在配置文件中定义个一个配置信息节,通过它在运行时装载我们的provider,像这样:
1 <configuration> 2 <forums> 3 <forums defaultProvider="SqlForumsProvider" defaultLanguage="en-en" > 5 <providers> 6 <clear/> 7 <add name = "SqlForumsProvider" type = "AspNetForums.Data.SqlDataProvider, AspNetForums.SqlDataProvider" 10 connectionString = "[connection string]" 11 databaseOwner = "dbo"/> 13 <add name = "AccessForumsProvider" type = "AspNetForums.Data.AccessDataProvider, AspNetForums.AccessDataProvider" 16 fileLocation = "~\data\AccessDataProvider\AspNetForums.mdb"/> 19 </providers> 20 </forums> 21 </forums> 22 </configuration>
当然,光有provider还不行,回想一下我们前面所说的provider的存在是为了给上层api提供具体的行为逻辑,所以我们还缺一个上层api,它们就是最终暴露给用户直接使用的应用程序接口,比如Membership类,比如SessionState类。
. pattern in real
我们来简单的小节下,看看现在我们手上已经都有哪些东西了:
1). 一个封装了一系列对外公布的api接口的service类,它们代表了应用程序能够提供的服务,比如Membership;
2). 一个ProviderBase基类,它封装了所有provider共有的属性和行为;
3). 一个继承自ProviderBase的类,它通常特定于我们的应用程序,里面提供的功能几乎就是servcice类的一个镜像,我们把它叫做ApplicationProviderBase类,比如MembershipProviderBase;
4). 一个继承自ApplicationProviderBase的类,它是我们真正具体的provider,比如SqlServerMembershipProvider;
上面这些组件便是组成我们asp.net中的provider模式不可或缺的东西,接下来我们来看看它们在运行时是如何协同工作的吧。关于这部分更为详细的描述你可以参考这一篇文章。像文中那样,我们以Membership服务为例进行说明。我们的Membership服务提供了很多方便的功能让应用程序可以对用户认证的数据进行统一的管理,比如创建一个用户(CreateUser)或者验证一个用户(ValidateUser)等等。现在应用程序调用了Membership.ValidateUser()方法,方法的内部有可能是这样子的:
1 public static bool Validate (string username, string password) 2 { 3 MembershipProvider provider = MembershipProvider.Instance(); 4 return provider.Validate (username, password); 5 }
于是接下来Instance()方法会被调用,内部的实现可能会是由一个叫做ProviderHelper的工具类实现(它在System.Web.Configuration名称空间中)。它完成了从配置文件中读取所需要的信息并根据这些信息实例化具体provider类的工作。每个provider类可以选择重载基类中的Initialize方法以提供自己的实例化实现。在得到了具体的provider实例之后,比如SqlServerMembershipProvider,我们的调用便会传递到这个实际的provider中去,在那里提供了特定于应用程序的逻辑实现,比如Validate方法会连接sql server数据库获取存储好的用户名和密码并与方法传递进来的参数进行比对以确定当前验证的用户是否是合法用户。
不知道各位同学,你对于provider模式有感觉了吗? :)
这里给出一些我觉得比较好的参考文档,大家有兴趣可以进一步阅读以获取更多关于provider模式的信息:
http://msdn.microsoft.com/en-us/library/aa479030.aspx ASP.NET 2.0 : Introduction to the Provider Model
http://msdn.microsoft.com/en-us/library/ms972319 Provider Model Design Pattern and Specification