• 收集的.Net文章(二十一)浅谈"三层结构"原理与用意(4)


        TraceLWord5中,加入了一个新项目SqlServerTask,这个项目的代码是访问的MS SQL Server 2000数据库。还有一个新建的项目DALFactory,这个项目就是一个“开关”。这个“开关”项目中仅有一个DbTaskDriver.cs程序文件,就是用它来控制TraceLWord5到底运行载那个数据库平台上?

    关于TraceLWord5,更完整的代码,可以在CodePackage/TraceLWord5目录中找到——

    DALFactory项目,其实就是“数据访问层工厂”,而DbTaskDriver类就是一个工厂类。也就是说DALFactory项目是“工厂模式”的一种应用。关于“工厂模式”,顾名思义,工厂是制造产品的地方,而“工厂模式”,就是通过“工厂类”来制造对象实例。“工厂类”可以通过给定的条件,动态地制造不同的对象实例。就好像下面这个样子:

     

    // 水果基类

    public class Fruit;

     

    // 苹果是一种水果

    public class Apple : Fruit;

     

    // 句子是一种水果

    public class Orange : Fruit;

    // 水果工厂类

    public class FruitFactory

    {

        // 根据水果名称制造一个水果对象

        public static Fruit CreateInstance(string fruitName)

        {

            if(fruitName=="APPLE")

                return new Apple();

            else if(fruiteName=="ORANGE")

                return new Orange();

            else

                return null;

        }

    }

    // 制造一个Apple对象,即:new Apple();

    Apple anApple=(Apple)FruitFactory.CreateInstance("APPLE");

    // 制造一个Orange对象,即:new Orange();

    Orange anOrange=(Orange)FruitFactory.CreateInstance("ORANGE");

     

      工厂类制造对象实例,实际通常是要通过语言所提供的RTTIRunTime Type Identification运行时类型识别)机制来实现。在Visual C#.NET中,是通过“反射”来实现的。它被封装在“System.Reflection”名称空间下,通过C#反射,我们可以在程序运行期间动态地建立对象。关于C#.NET反射,你可以到其它网站上搜索一下相关资料,这里就不详述了。左边是工厂模式的UML示意图。

     

     

     

    新建的DbTaskDriver.cs文件,位于DALFactory项目中

     

    #001 using System;

    #002 using System.Configuration;

    #003 using System.Reflection;   // 需要使用 .NET 反射

    #004

    #005 namespace TraceLWord5.DALFactory

    #006 {

    #007    /// <summary>

    #008    /// DbTaskDriver 数据库访问层工厂

    #009    /// </summary>

    #010    public class DbTaskDriver

    #011    {

    #012        DbTaskDriver 构造器

    #020

    #021        /// <summary>

    #022        /// 驱动数据库任务对象实例

    #023        /// </summary>

    #024        public object DriveLWordTask()

    #025        {

    #026            // 获取程序集名称

    #027            string assemblyName=ConfigurationSettings.AppSettings["AssemblyName"];

    #028            // 获取默认构造器名称

    #029            string constructor=ConfigurationSettings.AppSettings["Constructor"];

    #030

    #031            // 建立 AccessTask 或者 SqlServerTask 对象实例

    #032            return Assembly.Load(assemblyName).CreateInstance(constructor, false);

    #033        }

    #034    }

    #035 }

     


    那么相应的,LWordService.cs程序文件也要做相应的修改。

     

    #001 using System;

    #002 using System.Data;

    #003

    #004 using TraceLWord5.AccessTask;

    #005 using TraceLWord5.Classes;     // 引用实体规范层

    #006 using TraceLWord5.DALFactory;  // 引用数据访问层工厂

    #007 using TraceLWord5.SqlServerTask;

    #008

    #009 namespace TraceLWord5.InterService

    #010 {

    ...

    #014    public class LWordService

    #015    {

    ...

    #020        public LWord[] ListLWord()

    #021        {

    #022            object dbTask=(new DbTaskDriver()).DriveLWordTask();

    #023

    #024            // 留言板运行在 Access 数据库平台上

    #025            if(dbTask is AccessTask.LWordTask)

    #026                return ((AccessTask.LWordTask)dbTask).ListLWord();

    #027

    #028            // 留言板运行在 MS SQL Server 数据库平台上

    #029            if(dbTask is SqlServerTask.LWordTask)

    #030                return ((SqlServerTask.LWordTask)dbTask).GetLWords();

    #031

    #032            return null;

    #033        }

    ...

    #039        public void PostLWord(LWord newLWord)

    #040        {

    #041            object dbTask=(new DbTaskDriver()).DriveLWordTask();

    #042

    #043            // 留言板运行在 Access 数据库平台上

    #044            if(dbTask is AccessTask.LWordTask)

    #045                ((AccessTask.LWordTask)dbTask).PostLWord(newLWord);

    #046

    #047            // 留言板运行在 MS SQL Server 数据库平台上

    #048            if(dbTask is SqlServerTask.LWordTask)

    #049                ((SqlServerTask.LWordTask)dbTask).AddNewLWord(newLWord);

    #050        }

    #051    }

    #052 }


    原来的AccessTask项目及程序文件不需要变化,只是多加了一个SqlServerTask项目。新项目中,也有一个LWordTask.cs程序文件,其内容是:

     

    #001 using System;

    #002 using System.Collections;

    #003 using System.Data;

    #004 using System.Data.SqlClient;   // 需要访问 MS SQL Server 数据库

    #005 using System.Web;

    #006

    #007 using TraceLWord5.Classes;     // 引用实体规范层

    #008

    #009 namespace TraceLWord5.SqlServerTask

    #010 {

    #011    /// <summary>

    #012    /// LWordTask 留言板任务类

    #013    /// </summary>

    #014    public class LWordTask

    #015    {

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

    #017        private const string DB_CONN=@"Server=127.0.0.1; uid=sa; pwd=;

    DataBase=TraceLWordDb";

    #018

    #019        /// <summary>

    #020        /// 读取 LWord 数据表,返回留言对象数组

    #021        /// </summary>

    #022        /// <returns></returns>

    #023        public LWord[] GetLWords()

    #024        {

    #025            // 留言对象集合

    #026            ArrayList lwordList=new ArrayList();

    #027

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

    #029

    #030            SqlConnection dbConn=new SqlConnection(DB_CONN);

    #031            SqlCommand dbCmd=new SqlCommand(cmdText, dbConn);

    #032

    #033            try

    #034            {

    #035                dbConn.Open();

    #036                SqlDataReader dr=dbCmd.ExecuteReader();

    #037

    #038                while(dr.Read())

    #039                {

    #040                    LWord lword=new LWord();

    #041


    #042                    // 设置留言编号

    #043                    lword.UniqueID=(int)dr["LWordID"];

    #044                    // 留言内容

    #045                    lword.TextContent=(string)dr["TextContent"];

    #046                    // 发送时间

    #047                    lword.PostTime=(DateTime)dr["PostTime"];

    #048

    #049                    // 加入留言对象到集合

    #050                    lwordList.Add(lword);

    #051                }

    #052            }

    #053            catch

    #054            {

    #055                throw;

    #056            }

    #057            finally

    #058            {

    #059                dbConn.Close();

    #060            }

    #061

    #062            // 将集合转型为数组并返回给调用者

    #063            return (LWord[])lwordList.ToArray(typeof(TraceLWord5.Classes.LWord));

    #064        }

    #065

    #066        /// <summary>

    #067        /// 发送留言信息到数据库

    #068        /// </summary>

    #069        /// <param name="newLWord">留言对象</param>

    #070        public void AddNewLWord(LWord newLWord)

    #071        {

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

    #073            if(newLWord==null || newLWord.TextContent==null || newLWord.TextContent=="")

    #074                throw new Exception("留言内容为空");

    #075

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

    #077

    #078            SqlConnection dbConn=new SqlConnection(DB_CONN);

    #079            SqlCommand dbCmd=new SqlCommand(cmdText, dbConn);

    #080

    #081            // 设置留言内容

    #082            dbCmd.Parameters.Add(new SqlParameter("@TextContent", SqlDbType.NText));

    #083            dbCmd.Parameters["@TextContent"].Value=newLWord.TextContent;

    #084


    #085            try

    #086            {

    #087                dbConn.Open();

    #088                dbCmd.ExecuteNonQuery();

    #089            }

    #090            catch

    #091            {

    #092                throw;

    #093            }

    #094            finally

    #095            {

    #096                dbConn.Close();

    #097            }

    #098        }

    #099    }

    #100 }

     

    特别指出的是,这个SqlServerTask中的LWordTask程序文件,也遵循“土豆炖牛肉盖饭”式的强制标准!

    TraceLWord5中,也需要配置Web.Config文件,需要加入自定义的键值:

     

    #001 <?xml version="1.0" encoding="utf-8" ?>

    #002 <configuration>

    #003

    #004 <system.web>

    #005    <identity impersonate="true" />

    #006    <compilation defaultLanguage="c#" debug="true" />

    #007     <customErrors mode="RemoteOnly" />

    #008 </system.web>

    #009

    #010 <appSettings>

    ...

    #026    <!--// SQLServer 2000 数据库任务程序集及驱动类名称 //-->

    #027    <add key="AssemblyName"

    #028        value="TraceLWord5.SqlServerTask" />

    #029    <add key="Constructor"

    #030        value="TraceLWord5.SqlServerTask.LWordTask" />

    #031

    #032 </appSettings>

    #033

    #034 </configuration>

     

    通过修改配置文件中的关键信息,就可以修改留言板的数据库运行平台。这样便做到了跨数据库平台的目的。


    用户在访问TraceLWord5ListLWord.aspx页面时序图:
     

    当一个用户访问TraceLWord5ListLWord.aspx页面的时候,会触发该页面后台程序中的Page_Load函数。而在该函数中调用了LWord_DataBind函数来获取留言板信息。由图中可以看到出,LWord_DataBind在被调用的期间,会建立一个新的LWordService类对象,并调用这个对象的ListLWord函数。在LWordService.ListLWord函数被调用的期间,会建立一个新的DALFactory.DbTaskDriver类对象,并调用这个对象的DriveLWordTask函数来建立一个真正的数据访问层对象。在代码中,DriveLWordTask函数需要读取应用程序配置文件。当一个真正的数据访问层类对象被建立之后,会返给调用者LWordService.ListLWord,调用者会继续调用这个真正的数据访问层类对象的GetLWords函数,最终取到留言板数据。PostLWord.aspx页面时序图,和上面这个差不多。就是这样,经过一层又一层的调用,来获取返回结果或是保存数据。

     

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


    烹制土豆烧牛肉盖饭的方法论

    TraceLWord5已经实现了跨数据库平台的目的。但是稍微细心一点就不难发现,TraceLWord5有一个很致命的缺点。那就是如果要加入对新的数据库平台的支持,除去必要的新建数据访问层项目以外,还要在中间业务层InsetService项目中添加相应的依赖关系和代码。例如,新加入了对Oracle9i的数据库支持,那么除去要新建一个OracleTask项目以外,还要在LWordService中添加对OracleTask项目的依赖关系,并增加代码如下:

     

    ...

    #020        public LWord[] ListLWord()

    #021        {

    #022            object dbTask=(new DbTaskDriver()).DriveLWordTask();

    #023

    #024            // 留言板运行在 Access 数据库平台上

    #025            if(dbTask is AccessTask.LWordTask)

    #026                return ((AccessTask.LWordTask)dbTask).ListLWord();

    #027

    #028            // 留言板运行在 MS SQL Server 数据库平台上

    #029            if(dbTask is SqlServerTask.LWordTask)

    #030                return ((SqlServerTask.LWordTask)dbTask).GetLWords();

    #031

    #032            // 留言板运行在 Oracle 数据库平台上

    #033            if(dbTask is OracleTask.LWordTask)

    #034                return ((OracleTask.LWordTask)dbTask).FetchLWords();

    #035

    #036            return null;

    #037        }

    #038

    ...

     

    每加入对新数据库的支持,就要修改中间业务层,这是件很麻烦的事情。再有就是,这三个数据访问层,获取留言板信息的方法似乎是各自为政,没有统一的标准。在AccessTask项目中使用的是ListLWord函数来获取留言信息;而在SqlServerTask项目中则是使用GetLWords函数来获取;再到了OracleTask又是换成了FetchLWords……

    餐馆服务生也许会对新来的大厨师傅很感兴趣,或许也会对新来的大厨师傅的手艺很感兴趣。但是这些餐馆服务生,绝对不会去背诵哪位大厨师傅会做什么样的菜,哪位大厨师傅不会做什么样的菜?也不会去在意同样的一道菜肴,两位大厨师傅不同的烹制步骤是什么?对于我所点的“土豆炖牛肉盖饭”,餐馆服务生只管对着厨房大声叫道:“土豆炖牛盖饭一份!”,饭菜马上就会做好。至于是哪个厨师做出来的,服务生并不会关心。其实服务生的意思是说:“外面有个顾客要吃‘土豆炖牛肉盖饭’,你们两个大厨师傅,哪位会做这个,马上给做一份……”。如果新来的大厨师傅不会做,那么原来的大厨师傅会担起此重任。如果新来的大厨师傅会做,那么两个大厨师傅之间谁现在更悠闲一些就由谁来做。

    TraceLWord5中,两个数据访问层,都可以获取和保存留言信息,只是他们各自的函数名称不一样。但是对于中间业务层,却必须详细的记录这些,这似乎显得有些多余。仅仅是为了顺利的完成TraceLWord5这个“大型项目”,负责中间业务层的程序员要和负责数据访问层的程序员进行额外的沟通。TraceLWord5中,一个真正的数据访问层对象实例,是由DALFactory名称空间中的DbTaskDriver类制造的。如果中间业务层只需要知道“这个真正的数据访问层对象实例”有能力获取留言板和存储留言板,而不用关心其内部实现,那么就不会随着数据访问层项目的增加,而修改中间业务层了。换句直白的话来说就是:如果所有的数据访问层对象实例,都提供统一的函数名称“ListLWord函数”和“PostLWord函数”,那么中间业务层就不需要判断再调用了。我们需要“烹制土豆烧牛肉盖饭的方法论”的统一!——


    烹制土豆炖牛肉盖饭方法论的统一——接口实现

    怎么实现“烹制土豆烧牛肉盖饭方法论”的统一呢?答案是应用接口。在TraceLWord6中,新建了一个DbTask项目,里面只有一个ILWordTask.cs程序文件,在这里定义了一个接口。DbTask项目应该属于“抽象的数据访问层”。更完整的代码,可以在CodePackage/TraceLWord6目录中找到——


     

    当一个用户访问TraceLWord5ListLWord.aspx页面的时候,会触发该页面后台程序中的Page_Load函数。而在该函数中调用了LWord_DataBind函数来获取留言板信息。由图中可以看到出,LWord_DataBind在被调用的期间,会建立一个新的LWordService类对象,并调用这个对象的ListLWord函数。在LWordService.ListLWord函数被调用的期间,会建立一个新的DALFactory.DbTaskDriver类对象,并调用这个对象的DriveLWordTask函数来建立一个真正的数据访问层对象。在代码中,DriveLWordTask函数需要读取应用程序配置文件。当一个真正的数据访问层类对象被建立之后,会返给调用者LWordService.ListLWord,调用者会继续调用这个真正的数据访问层类对象的GetLWords函数,最终取到留言板数据。PostLWord.aspx页面时序图,和上面这个差不多。就是这样,经过一层又一层的调用,来获取返回结果或是保存数据。

     

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


    烹制土豆烧牛肉盖饭的方法论

    TraceLWord5已经实现了跨数据库平台的目的。但是稍微细心一点就不难发现,TraceLWord5有一个很致命的缺点。那就是如果要加入对新的数据库平台的支持,除去必要的新建数据访问层项目以外,还要在中间业务层InsetService项目中添加相应的依赖关系和代码。例如,新加入了对Oracle9i的数据库支持,那么除去要新建一个OracleTask项目以外,还要在LWordService中添加对OracleTask项目的依赖关系,并增加代码如下:

     

    ...

    #020        public LWord[] ListLWord()

    #021        {

    #022            object dbTask=(new DbTaskDriver()).DriveLWordTask();

    #023

    #024            // 留言板运行在 Access 数据库平台上

    #025            if(dbTask is AccessTask.LWordTask)

    #026                return ((AccessTask.LWordTask)dbTask).ListLWord();

    #027

    #028            // 留言板运行在 MS SQL Server 数据库平台上

    #029            if(dbTask is SqlServerTask.LWordTask)

    #030                return ((SqlServerTask.LWordTask)dbTask).GetLWords();

    #031

    #032            // 留言板运行在 Oracle 数据库平台上

    #033            if(dbTask is OracleTask.LWordTask)

    #034                return ((OracleTask.LWordTask)dbTask).FetchLWords();

    #035

    #036            return null;

    #037        }

    #038

    ...

     

    每加入对新数据库的支持,就要修改中间业务层,这是件很麻烦的事情。再有就是,这三个数据访问层,获取留言板信息的方法似乎是各自为政,没有统一的标准。在AccessTask项目中使用的是ListLWord函数来获取留言信息;而在SqlServerTask项目中则是使用GetLWords函数来获取;再到了OracleTask又是换成了FetchLWords……

    餐馆服务生也许会对新来的大厨师傅很感兴趣,或许也会对新来的大厨师傅的手艺很感兴趣。但是这些餐馆服务生,绝对不会去背诵哪位大厨师傅会做什么样的菜,哪位大厨师傅不会做什么样的菜?也不会去在意同样的一道菜肴,两位大厨师傅不同的烹制步骤是什么?对于我所点的“土豆炖牛肉盖饭”,餐馆服务生只管对着厨房大声叫道:“土豆炖牛盖饭一份!”,饭菜马上就会做好。至于是哪个厨师做出来的,服务生并不会关心。其实服务生的意思是说:“外面有个顾客要吃‘土豆炖牛肉盖饭’,你们两个大厨师傅,哪位会做这个,马上给做一份……”。如果新来的大厨师傅不会做,那么原来的大厨师傅会担起此重任。如果新来的大厨师傅会做,那么两个大厨师傅之间谁现在更悠闲一些就由谁来做。

    TraceLWord5中,两个数据访问层,都可以获取和保存留言信息,只是他们各自的函数名称不一样。但是对于中间业务层,却必须详细的记录这些,这似乎显得有些多余。仅仅是为了顺利的完成TraceLWord5这个“大型项目”,负责中间业务层的程序员要和负责数据访问层的程序员进行额外的沟通。TraceLWord5中,一个真正的数据访问层对象实例,是由DALFactory名称空间中的DbTaskDriver类制造的。如果中间业务层只需要知道“这个真正的数据访问层对象实例”有能力获取留言板和存储留言板,而不用关心其内部实现,那么就不会随着数据访问层项目的增加,而修改中间业务层了。换句直白的话来说就是:如果所有的数据访问层对象实例,都提供统一的函数名称“ListLWord函数”和“PostLWord函数”,那么中间业务层就不需要判断再调用了。我们需要“烹制土豆烧牛肉盖饭的方法论”的统一!——


    烹制土豆炖牛肉盖饭方法论的统一——接口实现

    怎么实现“烹制土豆烧牛肉盖饭方法论”的统一呢?答案是应用接口。在TraceLWord6中,新建了一个DbTask项目,里面只有一个ILWordTask.cs程序文件,在这里定义了一个接口。DbTask项目应该属于“抽象的数据访问层”。更完整的代码,可以在CodePackage/TraceLWord6目录中找到——


     

    通过数据访问层工厂来制造对象实例,而工厂类

    #001 <?xml version="1.0" encoding="utf-8" ?>

    #002 <configuration>

    ...

    #010 <appSettings>

    ...

    #026    <!--// SQLServer 2000 数据库任务程序集及驱动类名称 //-->

    #027    <add key="AssemblyName"

    #028        value="TraceLWord6.SqlServerTask" />

    #029    <add key="Constructor"

    #030        value="TraceLWord6.SqlServerTask.LWordTask" />

    #031

    #032 </appSettings>

    #033

    #034 </configuration>

    4

    5

    6

    7

    4

    5

    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        }

     

    在发送留言之前,调用“商业规则层”来验证当前行为是否有效?如果无效则会抛出一个异常。
    “三层结构”的缺点

    有些网友在读完这篇文章前作之后,对我提出了一些质疑,这提醒我文章至此还没有提及“三层结构”的缺点。“三层结构”这个词眼似乎一直都很热门,究其原因,或许是这种开发模式应用的比较普遍。但是“三层结构”却并不是百试百灵的“万灵药”,它也存在着缺点。下面就来说说它的缺点……

    “三层结构”开发模式的一个非常明显的缺点就是其执行速度不够快。当然这个“执行速度”是相对于非分层的应用程序来说的。从文中所给出的时序图来看,也明显的暴露了这一缺点。TraceLWord1TraceLWord2没有分层,直接调用的ADO.NET所提供的类来获取数据。但是,TraceLWord6确要经过多次调用才能获取到数据。在子程序模块程序没有返回时,主程序模块只能处于等待状态。所以在执行速度上,留言板的版本越高,排名却越靠后。“三层结构”开发模式,不适用于对执行速度要求过于苛刻的系统,例如:在线订票,在线炒股等等……它比较擅长于商业规则容易变化的系统。

    “三层结构”开发模式,入门难度够高,难于理解和学习。这是对于初学程序设计的人来说的。以这种模式开发出来的软件,代码量通常要稍稍多一些。这往往会令初学者淹没在茫茫的代码之中。望之生畏,对其产生反感,也是可以理解的……

    其实,无论哪一种开发模式或方法,都是有利有弊的。不会存在一种“万用法”可以解决任何问题。所以“三层结构”这个词眼也不会是个例外!是否采用这个模式进行系统开发,要作出比较、权衡之后才可以。切忌滥用——

     

    结束语

    谈到这里,文章对“三层结构”的原理和用意已经作了完整的阐述。作为这篇文章的作者,在心喜之余也感到写作技术文章并不是件很轻松的事情,特别是第一次写作像这样长达40多页的文章。为了能使读者轻松阅读,每字每句都要斟酌再三,唯恐会引起歧义。在这里要特别感谢一直关注和支持彬月论坛的网友,他们对彬月论坛的喜爱以及对我的支持,是我写作的巨大动力。当然,在这里还要感谢自己的父母,在我辞去原来的工作在家中完成彬月论坛的日子里,他们给了我极大的支持和理解……


      希望这篇文章能将你带到梦想的地方——

      AfritXia01.18/2005

     

     

     

    作  者:AfritXia

    网站地址:http://www.afritxia.net/http://www.bincess.cn/

    联系方式:AfritXia@HotMail.comAfritXia@Bincess.CNAfritXia2003@Yahoo.com.cn

    QQ联系:365410315

  • 相关阅读:
    MySQL之权限管理
    CentOS7使用firewalld打开关闭防火墙与端口
    CentOS 7.2 基于Docker实现MySQL主从架构
    Centos7下安装Docker
    nginx php-fpm安装配置 CentOS编译安装php7.2
    php7的扩展库安装方法
    Android 程序打包及签名
    Message和handler传递对象
    Android AlertDialog去除黑边白边自定义布局(转)
    用MVC做支付宝手机网页支付问题
  • 原文地址:https://www.cnblogs.com/xbf321/p/880609.html
Copyright © 2020-2023  润新知