标题:三层结构”原理与用意“ (二)
取消只看楼主
liang_0429
Rank: 1
等 级:新手上路
帖 子:18
专家分:0
注 册:2007-5-9
 问题点数:0 回复次数:1 
三层结构”原理与用意“ (二)

继续————》
为什么需要“三层结构”?——初探,就从数据库的升迁开始
一个站点中,访问数据库的程序代码散落在各个页面中,就像夜空中的星星一样繁多。这样一动百动的维护,难度可想而知。最难以忍受的是,对这种维护工作的投入,是没有任何价值的……
有一个比较好的解决办法,那就是将访问数据库的代码全部都放在一个程序文件里。这样,数据库平台一旦发生变化,那么只需要集中修改这一个文件就可以了。我想有点开发经验的人,都会想到这一步的。这种“以不变应万变”的做法其实是简单的“门面模式”的应用。如果把一个网站比喻成一家大饭店,那么“门面模式”中的“门面”,就像是饭店的服务生,而一个网站的浏览者,就像是一个来宾。来宾只需要发送命令给服务生,然后服务生就会按照命令办事。至于服务生经历了多少辛苦才把事情办成?那个并不是来宾感兴趣的事情,来宾们只要求服务生尽快把自己交待事情办完。我们就把ListLWord.aspx.cs程序就看成是一个来宾发出的命令,而把新加入的LWordTask.cs程序看成是一个饭店服务生,那么来宾发出的命令就是:
“给我读出留言板数据库中的数据,填充到DataSet数据集中并显示出来!”
而服务生接到命令后,就会依照执行。而PostLWord.aspx.cs程序,让服务生做的是:
“把我的留言内容写入到数据库中!”
而服务生接到命令后,就会依照执行。这就是TraceLWord2!可以在CodePackage/TraceLWord2目录中找到——

把所有的有关数据访问的代码都放到LWordTask.cs文件里,LWordTask.cs程序文件如下:

#001 using System;
#002 using System.Data;
#003 using System.Data.OleDb; // 需要操作 Access 数据库
#004 using System.Web;
#005
#006 namespace TraceLWord2
#007 {
#008 /// <summary>
#009 /// LWordTask 数据库任务类
#010 /// </summary>
#011 public class LWordTask
#012 {
#013 // 数据库连接字符串
#014 private const string DB_CONN=@"PROVIDER=Microsoft.Jet.OLEDB.4.0;
DATA Source=C:\DbFs\TraceLWordDb.mdb";
#015
#016 /// <summary>
#017 /// 读取数据库表 LWord,并填充 DataSet 数据集
#018 /// </summary>
#019 /// <param name="ds">填充目标数据集</param>
#020 /// <param name="tableName">表名称</param>
#021 /// <returns>记录行数</returns>
#022 public int ListLWord(DataSet ds, string tableName)
#023 {
#024 string cmdText="SELECT * FROM [LWord] ORDER BY [LWordID] DESC";
#025
#026 OleDbConnection dbConn=new OleDbConnection(DB_CONN);
#027 OleDbDataAdapter dbAdp=new OleDbDataAdapter(cmdText, dbConn);
#028
#029 int count=dbAdp.Fill(ds, tableName);
#030
#031 return count;
#032 }
#033
#034 /// <summary>
#035 /// 发送留言信息到数据库
#036 /// </summary>
#037 /// <param name="textContent">留言内容</param>
#038 public void PostLWord(string textContent)
#039 {
#040 // 留言内容不能为空
#041 if(textContent==null || textContent=="")
#042 throw new Exception("留言内容为空");
#043
#044 string cmdText="INSERT INTO [LWord]([TextContent]) VALUES(@TextContent)";
#045
#046 OleDbConnection dbConn=new OleDbConnection(DB_CONN);
#047 OleDbCommand dbCmd=new OleDbCommand(cmdText, dbConn);
#048
#049 // 设置留言内容
#050 dbCmd.Parameters.Add(new OleDbParameter("@TextContent", OleDbType.LongVarWChar));
#051 dbCmd.Parameters["@TextContent"].Value=textContent;
#052
#053 try
#054 {
#055 dbConn.Open();
#056 dbCmd.ExecuteNonQuery();
#057 }
#058 catch
#059 {
#060 throw;
#061 }
#062 finally
#063 {
#064 dbConn.Close();
#065 }
#066 }
#067 }
#068 }

如果将数据库从Access 2000修改为SQL Server 2000,那么只需要修改LWordTask.cs这一个文件。如果LWordTask.cs文件太大,也可以把它切割成几个文件或“类”。如果被切割成的“类”还是很多,也可以把这些访问数据库的类放到一个新建的“项目”里。当然,原来的ListLWord.aspx.cs文件应该作以修改,LWord_DataBind函数被修改成:

...
#046 private void LWord_DataBind()
#047 {
#048 DataSet ds=new DataSet();
#049 (new LWordTask()).ListLWord(ds, @"LWordTable");
#050
#051 m_lwordListCtrl.DataSource=ds.Tables[@"LWordTable"].DefaultView;
#052 m_lwordListCtrl.DataBind();
#053 }
...

原来的PostLWord.aspx.cs文件也应作以修改,Post_ServerClick函数被修改成:

...
#048 private void Post_ServerClick(object sender, EventArgs e)
#049 {
#050 // 获取留言内容
#051 string textContent=this.m_txtContent.Value;
#052
#053 (new LWordTask()).PostLWord(textContent);
#054
#055 // 跳转到留言显示页面
#056 Response.Redirect("ListLWord.aspx", true);
#057 }
...

  从前面的程序段中可以看出,ListLWord.aspx.cs和PostLWord.aspx.cs这两个文件已经找不到和数据库相关的代码了。只看到一些和LWordTask类有关系的代码,这就符合了“设计模式”中的一种重要原则:“迪米特法则”。“迪米特法则”主要是说:让一个“类”与尽量少的其它的类发生关系。在TraceLWord1中,ListLWord.aspx.cs这个类和OleDbConnection及OleDbDataAdapter都发生了关系,所以它破坏了“迪米特法则”。利用一个“中间人”是“迪米特法则”解决问题的办法,这也是“门面模式”必须遵循的原则。下面就引出这个LWordTask门面类的示意图:

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)...
}
}

用户在访问TraceLWord3的ListLWord.aspx页面时序图:

当一个用户访问TraceLWord5的ListLWord.aspx页面的时候,会触发该页面后台程序中的Page_Load函数。而在该函数中调用了LWord_DataBind函数来获取留言板信息。由图中可以看到出,LWord_DataBind在被调用的期间,会建立一个新的LWordService类对象,并调用这个对象的ListLWord函数。在LWordService.ListLWord函数被调用的期间,会建立一个新的LWordTask类对象,并调用这个对象的ListLWord来获取留言板信息的。PostLWord.aspx页面时序图,和上面这个差不多。就是这样,经过一层又一层的调用,来获取返回结果或是保存数据。

注意:从时序图中可以看出,当子程序模块未执行结束时,主程序模块只能处于等待状态。这说明将应用程序划分层次,会带来其执行速度上的一些损失……

对“三层结构”的深入理解——怎样才算是一个符合“三层结构”的Web应用程序?
在一个ASP.NET Web应用程序解决方案中,并不是说有aspx文件、有dll文件、还有数据库,就是“三层结构”的Web应用程序,这样的说法是不对的。也并不是说没有对数据库进行操作,就不是“三层结构”的。其实“三层结构”是功能实现上的三层。例如,在微软的ASP.NET示范实例“Duwamish7”中,“表现层”被放置在“Web”项目中,“中间业务层”是放置在“BusinessFacade”项目中,“数据访问层”则是放置在“DataAccess”项目中……而在微软的另一个ASP.NET示范实例“PetShop3.0”中,“表现层”被放置在“Web”项目中,“中间业务层”是放置在“BLL”项目中,而“数据访问层”则是放置在“SQLServerDAL”和“OracleDAL”两个项目中。在Bincess.CN彬月论坛中,“表现层”是被放置在“WebForum”项目中,“中间业务(服务)层”是被放置在“InterService”项目中,而“数据访问层”是被放置在“SqlServerTask”项目中。
  如果只以分层的设计角度看,Duwamish7要比PetShop3.0复杂一些!而如果较为全面的比较二者,PetShop3.0则显得比较复杂。但我们先不讨论这些,对PetShop3.0和Duwamish7的研究,并不是本文的重点。现在的问题就是:既然“三层结构”已经被分派到各自的项目中,那么剩下来的项目是做什么的呢?例如PetShop3.0中的“Model”、“IDAL”、“DALFactory”这三个项目,再例如Duwamish7中的“Common”项目,还有就是在Bincess.CN彬月论坛中的“Classes”、“DbTask”、这两个项目。它们究竟是做什么用的呢?
请关注三层结构”原理与用意“ (三)


忘能帮助大家 祝君成功!


搜索更多相关主题的帖子: 数据库 用意 原理 结构 星星 
2007-05-10 00:12
liang_0429
Rank: 1
等 级:新手上路
帖 子:18
专家分:0
注 册:2007-5-9
得分:0 

对于这些东东 确实是麻烦 好多的代码 看着都会晕
可是没有代码 空讲实在是说不出什么来 大家也不会得到什么
只是了解了以下三层的 概括介绍 如果想真正用上三层让它再我们的工程中
发挥它的优势 现在花一点时间也是值得的 是不是 呵呵
2007-05-15 19:34



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-138071-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.164809 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved