烹制土豆炖牛肉盖饭方法论的统一——接口实现
怎么实现“烹制土豆烧牛肉盖饭方法论”的统一呢?答案是应用接口。在TraceLWord6中,新建了一个DbTask项目,里面只有一个ILWordTask.cs程序文件,在这里定义了一个接口。DbTask项目应该属于“抽象的数据访问层”。更完整的代码,可以在CodePackage/TraceLWord6目录中找到——
DbTask项目中的ILWordTask.cs内容如下:
#001 using System;
#002
#003 using TraceLWord6.Classes; // 引用实体规范层
#004
#005 namespace TraceLWord6.DbTask
#006 {
...
#010 public interface ILWordTask
#011 {
#012 // 获取留言信息
#013 LWord[] ListLWord();
#014
#015 // 发送新留言信息到数据库
#016 void PostLWord(LWord newLWord);
#017 }
#018 }
AccessTask项目中的LWordTask.cs需要做出修改:
...
#007 using TraceLWord6.Classes; // 引用实体规范层
#008 using TraceLWord6.DbTask; // 引用抽象的数据访问层
#009
#010 namespace TraceLWord6.AccessTask
#011 {
...
#015 public class LWordTask : ILWordTask // 实现了ILWordTask接口
#016 {
...
#024 public LWord[] ListLWord()...
...
#071 public void PostLWord(LWord newLWord)...
...
#099 }
#100 }
SqlServerTask项目中的LWordTask.cs需要做出修改:
...
#007 using TraceLWord6.Classes; // 引用实体规范层
#008 using TraceLWord6.DbTask; // 引用抽象的数据访问层
#009
#010 namespace TraceLWord6.SqlServerTask
#011 {
...
#015 public class LWordTask : ILWordTask // 实现了ILWordTask接口
#016 {
...
#024 public LWord[] ListLWord()...
...
#071 public void PostLWord(LWord newLWord)...
...
#100 }
#101 }
AccessTask项目中的LWordTask类实现了ILWordTask接口,那么就必须覆写ListLWord和PostLWord这两个函数。SqlServerTask项目中的LWordTask类也实现了ILWordTask接口,那么就也必须覆写ListLWord和PostLWord这两个函数。这两个类对共同的接口ILWordTask的实现,使这两个类得到空前的统一。这对于求根溯源,向上转型也是很有帮助的。
DALFactory项目中的DbTaskDriver.cs文件也要作以修改:
...
#026 public ILWordTask DriveLWordTask()
#027 {
#028 // 获取程序集名称
#029 string assemblyName=ConfigurationSettings.AppSettings["AssemblyName"];
#030 // 获取默认构造器名称
#031 string constructor=ConfigurationSettings.AppSettings["Constructor"];
#032
#033 // 建立 ILWordTask 对象实例
#034 return (ILWordTask)Assembly.Load(assemblyName).CreateInstance(constructor,
false);
...
因为AccessTask项目中的LWordTask类和SqlServerTask项目中的LWordTask类,都实现了ILWordTask接口。那么,像行#034这样的转型是绝对成立的。而且转型后的对象,一定含有ListLWord和PostLWord这两个函数。InterService项目中的LWordService.cs程序文件应该作以修改,中间业务层只依赖于一个抽象的数据访问层。这样,修改具体的数据访问层就不会影响到它了:
...
#008 namespace TraceLWord6.InterService
#009 {
...
#013 public class LWordService
#014 {
#015 /// <summary>
#016 /// 读取 LWord 数据表,返回留言对象数组
#017 /// </summary>
#018 /// <returns></returns>
#019 public LWord[] ListLWord()
#020 {
#021 return (new DbTaskDriver()).DriveLWordTask().ListLWord();
#022 }
#023
#024 /// <summary>
#025 /// 发送留言信息到数据库
#026 /// </summary>
#027 /// <param name="newLWord">留言对象</param>
#028 public void PostLWord(LWord newLWord)
#029 {
#030 (new DbTaskDriver()).DriveLWordTask().PostLWord(newLWord);
#031 }
#032 }
#033 }
一次完整愉快的旅行
就让我们以ListLWord.aspx页面开始,进行一次完整愉快的旅行,看清TraceLWord6的运行全过程。当用浏览ListLWord.aspx页面时,服务器首先会调用ListLWord.aspx.cs文件:
...
#021 // 留言列表控件
#022 protected System.Web.UI.WebControls.DataList m_lwordListCtrl;
#023
#024 /// <summary>
#025 /// ListLWord.aspx 页面加载函数
#026 /// </summary>
#027 private void Page_Load(object sender, System.EventArgs e)
#028 {
#029 LWord_DataBind();
#030 }
...
#045 /// <summary>
#046 /// 绑定留言信息列表
#047 /// </summary>
#048 private void LWord_DataBind()
#049 {
#050 m_lwordListCtrl.DataSource=(new LWordService()).ListLWord();
#051 m_lwordListCtrl.DataBind();
#052 }
...
调用InterService名称空间中的LWordService类
...
#008 namespace TraceLWord6.InterService
#009 {
...
#013 public class LWordService
#016 /// 读取 LWord 数据表,返回留言对象数组
#017 /// </summary>
#018 /// <returns></returns>
#019 public LWord[] ListLWord()
#020 {
#021 return (new DbTaskDriver()).DriveLWordTask().ListLWord();
#022 }
...
#032 }
#033 }
通过数据访问层工厂来制造对象实例,而工厂类
DbTaskDriver需要读取网站应用程序中的:
Web.Config文件。这里应用了.NET反射机制。
...
#007 namespace TraceLWord6.DALFactory
#008 {
...
#012 public class DbTaskDriver
#013 {
...
#023 /// <summary>
#024 /// 驱动数据库任务对象实例
#025 /// </summary>
#026 public ILWordTask DriveLWordTask()
#027 {
#028 // 获取程序集名称
#029 string assemblyName=ConfigurationSettings.AppSettings["AssemblyName"];
#030 // 获取默认构造器名称
#031 string constructor=ConfigurationSettings.AppSettings["Constructor"];
#032
#033 // 建立 ILWordTask 对象实例
#034 return (ILWordTask)Assembly.Load(assemblyName).CreateInstance(constructor,
false);
#035 }
#036 }
#037 }
根据配置文件,制造TraceLWord6.SqlServerTask.LWordTask对象
...
#010 namespace TraceLWord6.SqlServerTask
#011 {
...
#015 public class LWordTask : ILWordTask
#016 {
...
#020 /// <summary>
#021 /// 读取 LWord 数据表,返回留言对象数组
#022 /// </summary>
#023 /// <returns></returns>
#024 public LWord[] ListLWord()...
...
#100 }
#101 }
最后按照页面上的代码样式绑定数据:
...
#018 <asp:DataList ID="m_lwordListCtrl" Runat="Server">
#019 <ItemTemplate>
#020 <div>
#021 <%# DataBinder.Eval(Container.DataItem, "PostTime") %>
#022 <%# DataBinder.Eval(Container.DataItem, "TextContent") %>
#023 </div>
#024 </ItemTemplate>
#025 </asp:DataList>
...
至此为止,一个简单的“三层结构”Web应用程序的执行全过程已经尽显在你眼前。执行顺序其实并不复杂。
加入商业规则
“商业规则”,是商业活动中的特殊规则。例如:我们去一家超市买东西,这家超市规定:凡是一次消费金额在2000元以上的顾客,可以获得一张会员卡。凭借这张会员卡,下次消费可以获得积分和享受9折优惠。“商业规则”主旨思想是在表达事与事之间,或者是物与物之间,再或者是事与物之间的关系,而不是事情本身或物质本身的完整性。再例如:一个用户在一个论坛进行新用户注册,该论坛系统规定,新注册的用户必须在4个小时之后才可以发送主题和回复主题。4个小时之内只能浏览主题。这也可以视为一种商业规则。但是,例如:电子邮件地址必须含有“@”字符;用户昵称必须是由中文汉字、英文字母、数字或下划线组成,这些都并不属于商业规则,这些应该被划作“实体规则”。它所描述的是物质本身的完整性。
在TraceLWord7中,商业规则是由Rules项目来实现的。其具体的商业规则是:
n 每天上午09时之后到11时之前可以留言,下午则是13时之后到17时之前可以留言
n 如果当天留言个数小于 40,则可以继续留言
这两个条件必须同时满足。更完整的代码,可以在CodePackage/TraceLWord7目录中找到——
那么,商业规则层和中间业务层有什么区别吗?其实本质上没有太大的区别,只是所描述的功能不一样。一个是功能逻辑实现,另外一个则是商业逻辑实现。另外,中间业务层所描述的功能逻辑通常是不会改变的。但是商业逻辑却会因为季节、消费者心理、资金费用等诸多因素而一变再变。把易变的部分提取出来是很有必要的。
LWordRules.cs文件内容:
#001 using System;
#002
#003 using TraceLWord7.Classes;
#004 using TraceLWord7.DALFactory;
#005 using TraceLWord7.DbTask;
#006
#007 namespace TraceLWord7.Rules
#008 {
#009 /// <summary>
#010 /// LWordRules 留言规则
#011 /// </summary>
#012 public class LWordRules
#013 {
#014 /// <summary>
#015 /// 验证是否可以发送新留言
#016 /// </summary>
#017 /// <returns></returns>
#018 public static bool CanPostLWord()
#019 {
...
#027 DateTime currTime=DateTime.Now;
#028
#029 // 每天上午 09 时之后到 11 时之前可以留言,
#030 // 下午则是 13 时之后到 17 时之前可以留言
#031 if(currTime.Hour<=8 || (currTime.Hour>=11 && currTime.Hour<=12) || currTime.Hour>=17)
#032 return false;
#033
#034 // 获取当天的留言个数
#035 LWord[] lwords=(new DbTaskDriver()).DriveLWordTask().ListLWord(
#036 currTime.Date, currTime.Date.AddDays(1));
#037
#038 // 如果当天留言个数小于 40,则可以继续留言
#039 if(lwords==null || lwords.Length<40)
#040 return true;
#041
#042 return false;
#043 }
#044 }
#045 }
在LWordService.cs文件中,要加入这样的规则:
#025 /// <summary>
#026 /// 发送留言信息到数据库
#027 /// </summary>
#028 /// <param name="newLWord">留言对象</param>
#029 public void PostLWord(LWord newLWord)
#030 {
#031 if(!LWordRules.CanPostLWord())
#032 throw new Exception("无法发送新留言,您违反了留言规则");
#033
#034 (new DbTaskDriver()).DriveLWordTask().PostLWord(newLWord);
#035 }
在发送留言之前,调用“商业规则层”来验证当前行为是否有效?如果无效则会抛出一个异常。
“三层结构”的缺点
有些网友在读完这篇文章前作之后,对我提出了一些质疑,这提醒我文章至此还没有提及“三层结构”的缺点。“三层结构”这个词眼似乎一直都很热门,究其原因,或许是这种开发模式应用的比较普遍。但是“三层结构”却并不是百试百灵的“万灵药”,它也存在着缺点。下面就来说说它的缺点……
“三层结构”开发模式的一个非常明显的缺点就是其执行速度不够快。当然这个“执行速度”是相对于非分层的应用程序来说的。从文中所给出的时序图来看,也明显的暴露了这一缺点。TraceLWord1和TraceLWord2没有分层,直接调用的ADO.NET所提供的类来获取数据。但是,TraceLWord6确要经过多次调用才能获取到数据。在子程序模块程序没有返回时,主程序模块只能处于等待状态。所以在执行速度上,留言板的版本越高,排名却越靠后。“三层结构”开发模式,不适用于对执行速度要求过于苛刻的系统,例如:在线订票,在线炒股等等……它比较擅长于商业规则容易变化的系统。
“三层结构”开发模式,入门难度够高,难于理解和学习。这是对于初学程序设计的人来说的。以这种模式开发出来的软件,代码量通常要稍稍多一些。这往往会令初学者淹没在茫茫的代码之中。望之生畏,对其产生反感,也是可以理解的……
其实,无论哪一种开发模式或方法,都是有利有弊的。不会存在一种“万用法”可以解决任何问题。所以“三层结构”这个词眼也不会是个例外!是否采用这个模式进行系统开发,要作出比较、权衡之后才可以。切忌滥用——
Word教程网 | Excel教程网 | Dreamweaver教程网 | Fireworks教程网 | PPT教程网 | FLASH教程网 | PS教程网 |
HTML教程网 | DIV CSS教程网 | FLASH AS教程网 | ACCESS教程网 | SQL SERVER教程网 | C语言教程网 | JAVASCRIPT教程网 |
ASP教程网 | ASP.NET教程网 | CorelDraw教程网 |