浅谈“三层结构”原理与用意 5
listlword.aspx.cs和postlword.aspx.cs两个文件对数据库的访问,全部委托lwordtask类这个“中间人”来办理。利用“门面模式”,将页面类和数据库类进行隔离。这样就作到了页面类不依赖于数据库的效果。以一段比较简单的代码来描述这三个程序的关系:
public class listlword
{
private void lword_databind()
{
(new lwordtask()).listlword( ... );
}
}
public class postlword
{
private void post_serverclick(object sender, eventargs e)
{
(new lwordtask()).postlword( ... );
}
}
public class lwordtask
{
public dataset listlword(dataset ds)...
public void postlword(string textcontent)...
}
应用中间业务层,实现“三层结构”
前面这种分离数据访问代码的形式,可以说是一种“三层结构”的简化形式。因为它没有“中间业务层”也可以称呼它为“二层结构”。一个真正的“三层”程序,是要有“中间业务层”的,而它的作用是连接“外观层”和“数据访问层”。换句话说:“外观层”的任务先委托给“中间业务层”来办理,然后“中间业务层”再去委托“数据访问层”来办理……
那么为什么要应用“中间业务层”呢?“中间业务层”的用途有很多,例如:验证用户输入数据、缓存从数据库中读取的数据等等……但是,“中间业务层”的实际目的是将“数据访问层”的最基础的存储逻辑组合起来,形成一种业务规则。例如:“在一个购物网站中有这样的一个规则:在该网站第一次购物的用户,系统为其自动注册”。这样的业务逻辑放在中间层最合适:
在“数据访问层”中,最好不要出现任何“业务逻辑”!也就是说,要保证“数据访问层”的中的函数功能的原子性!即最小性和不可再分。“数据访问层”只管负责存储或读取数据就可以了。
在新tracelword3中,应用了“企业级模板项目”。把原来的lwordtask.cs,并放置到一个单一的项目里,项目名称为:accesstask。解决方案中又新建了一个名称为:interservice的项目,该项目中包含一个lwordservice.cs程序文件,它便是“中间业务层”程序。为了不重复命名,tracelword3的网站被放置到了webui项目中。更完整的代码,可以在codepackage/tracelword3目录中找到——
这些类的关系,也可以表示为如下的示意图:
lwordservice.cs程序源码:
#001 using system;
#002 using system.data;
#003
#004 using tracelword3.accesstask; // 引用数据访问层
#005
#006 namespace tracelword3.interservice
#007 {
#008 /// <summary>
#009 /// lwordservice 留言板服务类
#010 /// </summary>
#011 public class lwordservice
#012 {
#013 /// <summary>
#014 /// 读取数据库表 lword,并填充 dataset 数据集
#015 /// </summary>
#016 /// <param name="ds">填充目标数据集</param>
#017 /// <param name="tablename">表名称</param>
#018 /// <returns>记录行数</returns>
#019 public int listlword(dataset ds, string tablename)
#020 {
#021 return (new lwordtask()).listlword(ds, tablename);
#022 }
#023
#024 /// <summary>
#025 /// 发送留言信息到数据库
#026 /// </summary>
#027 /// <param name="textcontent">留言内容</param>
#028 public void postlword(string content)
#029 {
#030 (new lwordtask()).postlword(content);
#031 }
#032 }
#033 }
从lwordservice.cs程序文件的行#021和行#030可以看出,“中间业务层”并没有实现什么业务逻辑,只是简单的调用了“数据访问层”的类方法……这样做是为了让读者更直观的看明白“三层结构”应用程序的调用顺序,看清楚它的全貌。加入了“中间业务层”,那么原来的listlword.aspx.cs文件应该作以修改:
...
#012 using tracelword3.interservice; // 引用中间服务层
...
#045 /// <summary>
#046 /// 绑定留言信息列表
#047 /// </summary>
#048 private void lword_databind()
#049 {
#050 dataset ds=new dataset();
#051 (new lwordservice()).listlword(ds, @"lwordtable");
#052
#053 m_lwordlistctrl.datasource=ds.tables[@"lwordtable"].defaultview;
#054 m_lwordlistctrl.databind();
#055 }
...
原来的postlword.aspx.cs文件也应作以修改:
...
#012 using tracelword3.interservice; // 引用中间服务层
...
#047 /// <summary>
#048 /// 发送留言到数据库
#049 /// </summary>
#050 private void post_serverclick(object sender, eventargs e)
#051 {
#052 // 获取留言内容
#053 string textcontent=this.m_txtcontent.value;
#054
#055 (new lwordservice()).postlword(textcontent);
#056
#057 // 跳转到留言显示页面
#058 response.redirect("listlword.aspx", true);
#059 }
...
到目前为止,tracelword3程序已经是一个简单的“三层结构”的应用程序,以一段比较简单的代码来描述四个程序的关系:
namespace tracelword3.weblword
{
public class listlword
{
private void lword_databind()
{
(new lwordservice()).listlword( ... );
}
}
public class postlword
{
private void post_serverclick(object sender, eventargs e)
{
(new lwordservice()).postlword( ... );
}
}
}
namespace tracelword3.interservice
{
public class lwordtask
{
public dataset listlword(dataset ds, string tablename)
{
return (new lwordtask()).listlword(ds, tablename);
}
public void postlword(string content)
{
(new lwordtask()).postlword(content);
}
}
}
namespace tracelword3.accesstask
{
public class lwordtask
{
public dataset listlword(dataset ds)...
public void postlword(string content)...
}
}