您的位置:首页 > 其它

业务逻辑层的设计(五)——事务脚本模式介绍

2013-11-20 21:19 344 查看
经过了几个月的奋斗,我终于又可以坐下来写点东西了。

业务逻辑层设计到此为止已经有第五篇了,其实我最想写的就是事务脚本这篇了,因为它最实在,适合各级别的开发人员,毫不夸张地讲,在企业应用的开发中,我觉得可以一招吃遍天下了。

事务脚本模式的思路:

通俗一点来说,就是你点了一个按钮触发一个事件时,这个事件中将处理一系列的命令,这些命令组合在一起形成一个事务,然后又可以在另一个按钮事件中重新将他们组合起来,形成一个新的事务。

比如,我的保存事件中执行一个事务,1检查一些状态;2保存一些数据;

然后我在另一个提交事件中也执行一个事务,1检查一些状态;2保存一些数据;3流程提交;

伪代码表示

public void Save(){
Dao1 dao1=new Dao1(); dao1.IsExists();//检查一些东西
Dao2 dao1=new Dao2(); dao2.Save();//保存一些数据
}

public void Submit(){
try{
BeginTran();
Dao1 dao1=new Dao1(); dao1.IsExists();//检查一些东西
Dao2 dao1=new Dao2(); dao2.Save();//保存一些数据
Dao3 dao3=new Dao3(); dao3.GoToNextFlow();//流程提交
Commit();
}catch(){
Rollback();
}
}


看完以上伪代码我们发现保存按钮的时候我们没有启用事务,
而在提交按钮的时候我们开启了事务,需要保证数据的保存和提交在一个事务中。

优势:

简单,容易上手,DAO(数据访问对象)的重用可能性高,强于经典的三层。

不管你是在浪迹职场咪咪笑笑的老程序员,还是楚楚可怜的新程序员,都会深深地爱上这种模式。

对于新程序员来说,在软件架构设计师的对框架的支持下,我们写SQL的时候不必关注事务的管理,可以集中精力对付功能的实现上。

对于老程序员来说,它十分灵活,可以将DAO改造成Command(命令)然后发挥各种设计模式,因为将事务管理移到了业务层,并且去掉了讨厌的ADO.NET事务相关的参数,使得老鸟发挥各种设计模式的时候更加随心所欲。

DAO或者Command摆脱了事务的困扰后,它的设计高度符合高内聚的设计原则,一个Command(命令)只做一件事情,可以放心地重用或者修改其他人写类。

也因为高内聚,单元测试时先测试每一个DAO,然后再测试事件,做到自底向上,逐步求精,这也充分符合了面向对象的测试。

劣势:

事物总是有两面性的,就因为它的优点做一件事情建立一个Command类,所以项目经历了1期、2期、3期。。。功能的扩展以后,你会发现项目中将会有一堆Command类,和一群Manager类,所以你得小心地给他们分门别类的存放。

与使用O/RM相比,还是需要写SQL语句,虽然可以重用数据访问的代码,因为没有领域模型,所以很多业务规则依然没办法重用。

尽管它有许多劣势,但与经典的三层相比,它可以处理更加复杂的业务需求,

经典的三层一旦遇到事务密集的项目,业务层和数据访问层就会黏在一起了,如果对三层进行改良,把事务管理提上来,放在业务层,然后思维模式稍加改变,就变成了事务脚本。

所以,如果你的需求不只是做一些简单增删改的页面,需要使用事务犹如家常便饭,然后又觉得引入O/RM会对现有的项目产生影响,那么你的开发框架可以考虑向事务脚本演化了。

事务管理的实现原理:

将事务管理从数据访问层上提,放到业务层,并且消除ADO.NET的事务参数。

如何实现?我们需要将一个数据库连接贯穿整个事件(或者是每个WEB请求),在事件中所有DAO都将使用同一个数据库连接。

我们只要利用CallContext类把数据库连接放到一个请求上下文中,DAO需要操作数据库时从CallContext中取出连接即可。

WEB应用程序中也可以直接使用HttpContext。

在老项目中最好是自己对DBHelper作扩展,简单地使用继承就能在不修改原来代码的基础上扩展出新的DBHelper。

如果你准备新开发一个系统,那么可以选择使用Spring或者Spring.NET,参看事务管理的那部分,将事务托管给Spring来管理。

事务脚本模式实战:

某一个按钮事件,可以发现事务由N个命令组成,然后AOGTypeFlowCreate.Init小用了一下工厂模式,然而一系列的命令访问数据库的操作都在同一个事务中。

public void Appiont(decimal? schemeid,decimal? aogtype)
{
try
{
BeginTransaction();
GetCurrToProviderFlowCMD getcurrToProviderCMD = new GetCurrToProviderFlowCMD(schemeid);
getcurrToProviderCMD.Run();         //验证流程1是否已开始
if (getcurrToProviderCMD.CurrFlow > 0) throw new Exception("流程1已经开始,无法重新指定!");
GetCurrToStorageFlowCMD getcurrToStorageCMD = new GetCurrToStorageFlowCMD(schemeid);
getcurrToStorageCMD.Run();         //验证流程2是否已开始
if (getcurrToStorageCMD.CurrFlow > 0) throw new Exception("流程2已经开始,无法重新指定!");
AOGTypeFlowDeleteCMD aogtypeflowdelCMD = new AOGTypeFlowDeleteCMD(schemeid);
aogtypeflowdelCMD.Run();            //删除流程任务
AOGTypeAppointCMD aogtypeappointCMD = new AOGTypeAppointCMD(schemeid, aogtype);
if (aogtypeappointCMD.Run() == 0) throw new DataBaseExecuteInvalidException();      //指定到货方式
if (aogtype != null)
{
IAOGTypeFlowCreateCMD aogtypeflowcreateCMD = AOGTypeFlowCreate.Init(schemeid, aogtype);
if (aogtypeflowcreateCMD.Run() == 0) throw new DataBaseExecuteInvalidException();        //插入新的流程任务
}
Commit();
        }
catch (Exception ex)
{
RollBack();
throw new Exception(ex.Message);
}
}


某一个命令,以构造函数初始化参数,用只读属性返回值,于是所有的命令都有一个统一的方法Run(),实战中我用的命令模式的简化版。

操作数据库的database是由DBHelper扩展而来,如何扩展就见仁见智,根据上面提到的原理不难实现。

public class GetCurrToProviderFlowCMD : BaseCommand
{
private decimal? _schemeid;

private int _currFlow;

public int CurrFlow
{
get { return _currFlow; }
}

public GetCurrToProviderFlowCMD(decimal? schemeid)
{
_schemeid = schemeid;
}

public int Run()
{
StringBuilder builder = new StringBuilder();
builder.Append("select currflow from oproviderflow where schemeid=:schemeid");
OracleParameter prm = new OracleParameter(":schemeid", OracleDbType.Decimal);
prm.Value = _schemeid;
string res = database.ExecuteScalar(builder.ToString(), prm);
_currFlow = res != "" ? Convert.ToInt32(res) : 0;
return 1;
}
}


下面是使用微软企业库实现的版本

public class FirstCommand:BaseCommand
{
private DataTable _table;

private int _id;

public DataTable Table
{
get { return _table; }
}

public FirstCommand(int id)
{
this._id = id;
}

public int Run()
{
IDbCommand cmd=dbHelper.GetSqlStringCommand("select * from SYS_RESOURCE  where ID=:resid");
dbHelper.AddInParameter(cmd, ":resid", DbType.Int32, _id);
_table = dbHelper.ExecuteDataTable(cmd);
return 1;
}
}


这里并没有给出我具体怎么扩展框架的代码,非常遗憾,但是解决思路才是最重要的不是吗?

对自己没有信心的话,完全可以引入Spring.NET框架使用AdoTemplate来解决问题,学习框架or自己动手,只要能解决问题都可行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: