关于三层架构 = 门面模式/外观模式

原创 2012年03月23日 15:47:59

“三层结构”是什么?

  “三层结构”一词中的“三层”是指:“表现层”、“中间业务层”、“数据访问层”。其中:

n           表 现 层:位于最外层(最上层),离用户最近。用于显示数据和接收用户输入的数据,为用户提供一种交互式操作的界面。

n           中间业务层:负责处理用户输入的信息,或者是将这些信息发送给数据访问层进行保存,或者是调用数据访问层中的函数再次读出这些数据。中间业务层也可以包括一些对“商业逻辑”描述代码在里面。

n           数据访问层:仅实现对数据的保存和读取操作。数据访问,可以访问数据库系统、二进制文件、文本文档或是XML文档。 

  对依赖方向的研究将是本文的重点,数值返回方向基本上是没有变化的。

  如果只以分层的设计角度看,Duwamish7要比PetShop3.0复杂一些!而如果较为全面的比较二者,PetShop3.0则显得比较复杂。但我们先不讨论这些,对PetShop3.0Duwamish7的研究,并不是本文的重点。现在的问题就是:既然“三层结构”已经被分派到各自的项目中,那么剩下来的项目是做什么的呢?例如PetShop3.0中的“Model”、“IDAL”、“DALFactory”这三个项目,再例如Duwamish7中的“Common”项目,还有就是在Bincess.CN彬月论坛中的“Classes”、“DbTask”、这两个项目。它们究竟是做什么用的呢?

 

对“三层结构”的深入理解——从一家小餐馆说起

  一个“三层结构”的Web应用程序,就好象是一家小餐馆。

n           表 现 层,所有的.aspx页面就好像是这家餐馆的菜谱。

n           中间业务层,就像是餐馆的服务生。

n           数据访问层,就像是餐馆的大厨师傅。

n           而我们这些网站浏览者,就是去餐馆吃饭的吃客了……

 

 

我们去一家餐馆吃饭,首先得看他们的菜谱,然后唤来服务生,告诉他我们想要吃的菜肴。服务生记下来以后,便会马上去通知大厨师傅要烹制这些菜。大厨师傅收到通知后,马上起火烧菜。过了不久,服务生便把一道一道香喷喷的、热气腾腾的美味端到我们的桌位上——

而我们访问一个基于asp.net技术的网站的时候,首先打开的是一个aspx页面。这个aspx页面的后台程序会去调用中间业务层的相应函数来获取结果。中间业务层又会去调用数据访问层的相应函数来获取结果。 

为什么需要“三层结构”?——初探,就从数据库的升迁开始

一个站点中,访问数据库的程序代码散落在各个页面中,就像夜空中的星星一样繁多。这样一动百动的维护,难度可想而知。最难以忍受的是,对这种维护工作的投入,是没有任何价值的……

有一个比较好的解决办法,那就是将访问数据库的代码全部都放在一个程序文件里。这样,数据库平台一旦发生变化,那么只需要集中修改这一个文件就可以了。我想有点开发经验的人,都会想到这一步的。这种“以不变应万变”的做法其实是简单的“门面模式”的应用。如果把一个网站比喻成一家大饭店,那么“门面模式”中的“门面”,就像是饭店的服务生,而一个网站的浏览者,就像是一个来宾。来宾只需要发送命令给服务生,然后服务生就会按照命令办事。至于服务生经历了多少辛苦才把事情办成?那个并不是来宾感兴趣的事情,来宾们只要求服务生尽快把自己交待事情办完。我们就把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    publicclass LWordTask

#012    {

#013        // 数据库连接字符串

#014        privateconststring 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        publicint ListLWord(DataSet ds,string tableName)

#023        {

#024            string cmdText="SELECT * FROM [LWord] ORDER BY [LWordID] DESC";

#025

#026            OleDbConnection dbConn=newOleDbConnection(DB_CONN);

#027            OleDbDataAdapter dbAdp=newOleDbDataAdapter(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        publicvoid PostLWord(string textContent)

#039        {

#040            // 留言内容不能为空

#041            if(textContent==null || textContent=="")

#042                thrownew Exception("留言内容为空");

#043

#044            string cmdText="INSERT INTO [LWord]([TextContent]) VALUES(@TextContent)";

#045

#046            OleDbConnection dbConn=newOleDbConnection(DB_CONN);

#047            OleDbCommand dbCmd=newOleDbCommand(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        privatevoid LWord_DataBind()

#047        {

#048            DataSet ds=new DataSet();

#049            (newLWordTask()).ListLWord(ds,@"LWordTable");

#050

#051            m_lwordListCtrl.DataSource=ds.Tables[@"LWordTable"].DefaultView;

#052            m_lwordListCtrl.DataBind();

#053        }

...

 

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

 

...

#048        privatevoid Post_ServerClick(object sender, EventArgs e)

#049        {

#050            // 获取留言内容

#051            string textContent=this.m_txtContent.Value;

#052

#053            (newLWordTask()).PostLWord(textContent);

#054

#055            // 跳转到留言显示页面

#056            Response.Redirect("ListLWord.aspx",true);

#057        }

...

 

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

 

 

ListLWord.aspx.csPostLWord.aspx.cs两个文件对数据库的访问,全部委托LWordTask类这个“中间人”来办理。利用“门面模式”,将页面类和数据库类进行隔离。这样就作到了页面类不依赖于数据库的效果。以一段比较简单的代码来描述这三个程序的关系:

 

public class ListLWord

{

private void LWord_DataBind()

{

    (newLWordTask()).ListLWord( ... );

    }

}

 

public class PostLWord

{

    private void Post_ServerClick(object sender, EventArgs e)

    {

        (newLWordTask()).PostLWord( ... );

    }

}

 

public class LWordTask

{

    public DataSet ListLWord(DataSet ds)...

 

    public void PostLWord(string textContent)...

}

 

应用中间业务层,实现“三层结构”

前面这种分离数据访问代码的形式,可以说是一种“三层结构”的简化形式。因为它没有“中间业务层”也可以称呼它为“二层结构”。一个真正的“三层”程序,是要有“中间业务层”的,而它的作用是连接“外观层”和“数据访问层”。换句话说:“外观层”的任务先委托给“中间业务层”来办理,然后“中间业务层”再去委托“数据访问层”来办理……

那么为什么要应用“中间业务层”呢?“中间业务层”的用途有很多,例如:验证用户输入数据、缓存从数据库中读取的数据等等……但是,“中间业务层”的实际目的是将“数据访问层”的最基础的存储逻辑组合起来,形成一种业务规则。例如:“在一个购物网站中有这样的一个规则:在该网站第一次购物的用户,系统为其自动注册”。这样的业务逻辑放在中间层最合适:

 

 

在“数据访问层”中,最好不要出现任何“业务逻辑”!也就是说,要保证“数据访问层”的中的函数功能的原子性!即最小性和不可再分。“数据访问层”只管负责存储或读取数据就可以了。

  在新TraceLWord3中,应用了“企业级模板项目”。把原来的LWordTask.cs,并放置到一个单一的项目里,项目名称为:AccessTask。解决方案中又新建了一个名称为:InterService的项目,该项目中包含一个LWordService.cs程序文件,它便是“中间业务层”程序。

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”项目中。

注:上文引自网上某位老兄的文章,找不原始出处了。


设计模式学习笔记—外观模式

外观模式(Facade pattern) 首先,为什么我们需要外观模式? 答:接口太多了,需要一个新的接口去统一这些接口。 我有个疑问就是,如果代码接口变多,显得有些乱,去统一一下接口不是很正常的...
  • LiynGu
  • LiynGu
  • 2016年01月21日 18:36
  • 315

三层架构结合外观模式实现登录

登录功能在设计的时候利用三层架构来分析整个业务实现,同时在UI层和BLL层之间增加一个业务外观层。这样子让两层明显的隔离,表示层的任何变化,比如是用客户端软件还是浏览器方式表示都不会影响到业务与数据的...

Facade 外观(门面)模式

Facade模式为子系统提供统一的对外接口,某种意义上讲,它代表着这个子系统。简化外部对子系统访问的复杂度。一个外观模式类封装了子系统对外的访问接口,但这并不完全隔断外部对子系统的接口调用,外部子系统...
  • golb_05
  • golb_05
  • 2011年10月15日 15:36
  • 180

Java设计模式——门面(外观)模式(Facade Pattern)

场景一 描述:好,我们继续讲课。大家都是高智商的人,都写过纸质的信件吧,比如给女朋友写情书什么的,写信的过程大家都还记得吧,先写信的内容,然后写信封,然后把信放到信封中,封好,投递到信箱中进行邮递,...

设计模式之 - 门面(外观)模式

一、定义         外观模式应该是用的很多的一种模式,特别是当一个系统很复杂时,系统提供给客户的是一个简单的对外接口,而把里面复杂的结构都封装了起来。客户只需使用这些简单接口就能使用这个系统,...

外观模式(门面模式)---脸有时比才华更重要

声明:文章内容根据大牛博客的内容,自己理解后,给自己做的学习笔记,文末会附上大牛博客地址链接。有需要沟通交流的可加我QQ群:425120333 外观模式也称作门面模式,是指对外提供一个...

门面模式(Facade)(外观模式)—向养牛场送货

门面模式 :为子系统构建与外部系统交互的统一界面。 门面模式的优点,我认为有两点:    1:解耦各个子系统,减少子系统与外部系统调用时的频繁调用,    2:屏蔽子系统内的方法,这样就可以尽量少的向...
  • wugouzi
  • wugouzi
  • 2015年12月08日 15:26
  • 337

java 设计模式第2弹--外观/门面模式

一、外观模式慨念 它为子系统中的一组接口提供一个统一的高层接口。是的子系统更容易使用 二、解决的问题 解决类与类之间的依赖关系,像spring一样,可以将类和类之间的关系配置到配...

门面模式/外观模式

原文链接:http://www.tutorialspoint.com/design_pattern/facade_pattern.htm Facade Pattern(门面模式/外观模式) 隐藏了系...

门面模式(外观Facade模式)

外观模式UML图 外观模式UML 定义 外观模式(也成为门面模式)要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。它提供一个高层次的接口,使得子系统更易于使用 类型...
  • Hynial
  • Hynial
  • 2015年08月16日 08:05
  • 282
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:关于三层架构 = 门面模式/外观模式
举报原因:
原因补充:

(最多只允许输入30个字)