CSLA业务对象入门[An Introduction to Business Objects in C#]
Posted on 2006-09-15 16:56 Phono 阅读(935) 评论(1) 编辑 收藏 举报今天翻译一篇文章,老外的东西却是先进,但他们也特别拽,我下载他们的代码很少能一下执行成功的,得来回来去的改,计较旮旯的一通溜你,不过改完也就明白个大概了,再看就有点自己原创的感觉了,不知道他们是不是有这个嗜好。言归正传,这次说的是CSLA,前一阵正好在研究,以后有时间会写些更详细的出来。
从这里/Files/phono/person_csla.rar下载源代码
这个例子是介绍Rockford Lhotka大哥的Component-based Scalable Logical Architecture (CSLA)设计思想的一个简单示例,有本书,叫《业务对象专家指南》,说的就是这个,说实话,不太好懂。CSLA实现的是一种N层框架,程序部署在四个逻辑层次——表现层,UI业务对象,数据业务对象和数据服务,这个框架的目的是要实现了两个方面的功能,
1.可扩展性
2.在不同物理层间灵活部署
后面一条的意思是我们能自由决定部署方式,可以把所有层都部署在一台机器上,或者在稍稍或根本不用修改代码的情况下把程序变为客户端——服务器模式的应用程序,这还说明了我们可以不用过多考虑表现层的性质,windows的也好,webPage也好,后台的业务逻辑代码和部署方式都不会有太大变化。
一个典型的物理框架结构是这样的,使用一个胖客户端作为用户接口,把UI业务对象部署在这个客户端工作站上使之能以最近距离服务用户接口,UI业务对象和部署在远端应用程序服务器上的数据业务对象进行通信,而数据业务对象会跟数据库服务器上的数据服务进行通信。
实例里的代码不是完整的重视与框架,这些代码只是简单的阐述了一些框架的基本设计思想,它实现了了一个简单的UI业务对象对一个公民的信息维护。社会保险号,姓名和出生日期,通过这是实例表现了一个 Microsoft .Net Windows Forms 用户接口如何同一个业务对象进行交互,并对用户输入的信息进行校验,用户接口不会输入信息作任何有关业务逻辑的校验,他只负责显示业务对象的属性,而不考虑其是不是经过校验的正确数据。
公民类
公民类包括三个供外界使用的属性:
- 社会保险号
- 姓名
- 出生年月
还包含一个计算得出的属性:
- 年龄
有三条业务规则
- 社会保险号只能是十一位的英文数字
- 姓名不能为空
- 生日格式必须是日期型,且时间合理。
一个公民类如果不满足以上三点,就不能将其置为合法状态
为了得到最丰富,响应效果最佳的用户体验,有一个想法就是在用户输入过程中就对其进行校验,这样比全部输入完成后再统一校验效果更好。基于其他的用户接口,如HTML 3.2接口,能够提供提交后的批量校验,而我们需要的是一个丰富而友善的用户接口,Windows型的GUI恰恰提供了这一切。
基本思想是,保存按钮只有在对所有控件的输入都合法的情况下才能变成有效。这显然是用户界面开发人员编写代码实现的,但他们不用编写具体的校验逻辑,他们只负责运行校验函数然后根据返回结果设置校验成功标志位。
在实际项目中,UI开发人员需要知道的业务逻辑当然更多,但他们需要做的不是编写业务逻辑,而只是根据情况实时改变公民实体的状态。
让我们来考虑一下公民类的操作会有哪些
- 添加一个新公民
- 把这个公民的信息保存到数据库里
- 从数据库中读出一个既存的公民数据
- 删除一个公民的信息
一个用户接口至少可以满足客户的一下要求:
- 编辑数据,保存变更并关闭应用程序
- 把这个公民的信息保存到数据库里
- 编辑数据,保存变更,并作进一步编辑,保存数据关闭应用程序
等等等等
由上述操作生成的数据实体一般来说会用到OK,Cancel 或Apply按钮。乍一看好像没什么特别的,然而CSLA框架的特性之一就是根据操作者和步骤的不同,公民对象的状态会随时改变,如果用户取消了输入,一个很重要问题就是要将对象中的数据恢复到前一个有效状态,这既意味着我们要对以前的操作结果进行保存。
这个实例虽不是一个很复杂的程序但通过对公民类的维护操作,将尽力给他家展现他的核心思想。
除了上边讲的,UI开发者还需要知道些其他的事,比如,这个公民是不是新生成的,是否被修改过,是否正在被编辑。
比如,一个正在被编辑的公民不能被从数据库中读出同类数据所覆盖。
编辑管理
为了使编辑功能完善,有三个方法是必须的:BeginEdit
This enables editing.
ApplyEdit
在适当或终止编辑的时候保存或删除对象,客户端应当调用BeginEdit 后才能进行编辑。
CancelEdit
取消自上次ApplyEdit 操作后所有的改变,并将对象定义为可消除状态,结束当前的编辑。
在数据库技术中,也有三个类似的方法,分别为 "BeginTrans", "Commit" and "Rollback",但区别在于一个作用于对象,一个作用于数据库。
管理业务规则
在Rockford Lhotka大神原版的框架中,他创建了一个BrokenRules 类,供每一个业务规则使用,但是在实例中,我将这一功能性模块放到了基类BusinessObject中,同时也可以使用抽象接口来定义编辑用的方法,但在例子里没有这么做。
一个公民对象是否合法取决于叫做broken business rules的一个collection ,它维护着违反规则的项目,当这个collection 不为空的时候,这个公民对象就是不合法的,当其中项目为零的时候,他自然就是一个“合法公民”了
维护这个collection的方法:
void RuleBroken(string rule, bool isBroken)
在其中
rule 是对业务规则的描述,它可能只是一个很简单的属性名,比如 "Birth Date"
isBroken 指示着这个业务规则是否被打破。
如果这个规则被大破,则将它加到collection中,如果是一个被重复打破的规则,通过算法控制忽略它,不把它加到collection中,如果是遵守了规则的合法情况,对应的该条记录将被从collection中删除。
当一个类刚刚被实例化的时候,所有的属性都是空,所以所有的业务规则都是被打破的,强调一下这些规则:
- 社会保险号只能是十一位的英文数字
- 姓名不能为空
- 生日格式必须是日期型,且时间合理。
所以在初始化公民类的时候,我们写道:
RuleBroken("Social Security Number", true);
RuleBroken("Name", true);
RuleBroken("Birth Date", true);
当然,这种情况是不常有的,按照常识来讲,当所有条件都具备,没有违反业务逻辑的情况下,对象才会被实例化出来,然而,这是一种特殊情况,当进行一些关键操作的时候,如将类持久化或还原,这一方法保证了类在这些操作前是合法的。
创建一个对公民的验证
设计思想是随着用户输入正确的信息一个公民类的状态将由不合法专为合法,,在初始状态,所有的业务校验
逻辑都处于错误的状态,,而后,随着用户逐个键入正确的信息,当所有的校验逻辑都被标记为正确以后,这个公民类的状态也就变成了有效。
要实现这一功能就要编写公民的SET某某属性事件,以及画面的TextChanged 事件
以社会保险号为例:
在Person.set_SocialSecurityNumber属性中
set
{
socialSecurityNumber = value;
RuleBroken("Social Security Number", socialSecurityNumber.Length != 11);
}
在 Form.txtSocialSecurityNumber_TextChanged
事件中.
person.SocialSecurityNumber = txtSocialSecurityNumber.Text;
TextChanged事件在每次输入触发中都会被调用,公民设置SocialSecurityNumber 属性的同时会调用RuleBroken
注意表达式的书写方式:socialSecurityNumber.Length != 11
.
当返回值为true的时候则说明它违反了业务规则。
当刚开始输入社会保险号的时间发生时,由于输入内容不正确,所以该条规则违反,因而将其加入到违反规则集合中。在接下来的输入中,由于依然违反规则,则忽略对他的处理,当11个值都输入完成以后,返回值为false,这也表示着该条规则不再是错误的了,于是就要将其从集合中删除。
在填写姓名和生日属性的时候这个过程还将重复,直到公民对象变为合法为止。
通知客户端一个公民是否合法
公民类具有验证标志属性,同时还抛出相应的自定义事件。这两个事件交由客户端处理,以Window Form应用程序为例,使公民对象可以保存或使之只读,是通过控制OK 和Apply 两个button的使能(enable)实现的。
为了触发该事件,公民类(实际上应该是基类)声明了两个事件句柄委托和两个事件。
public delegate void EventHandler(object sender, EventArgs e);
public event EventHandler OnInvalid;
public event EventHandler OnValid;
为了触发 OnValid
事件
if (OnValid != null)
{
OnValid (this, EventArgs.Empty);
}
当违反规则的数量降为0的时候,RuleBroken 就会执行这段代码
客户端的处理事件如下:
// Subscribe to Person event
person.OnValid += new Person.EventHandler(Person_OnValid);
private void Person_OnValid(object sender, System.EventArgs e)
{
btnOK.Enabled = true;
btnApply.Enabled = true;
}
这段代码与实际业务相比起来要简单的多,但也能点明设计思想了。
OnInvalid 事件处理与之相似
年龄属性
年龄属性很有趣,他被设置成了只读,这样客户端就不能够重新设置它的值,它的值通过计算生日获得,由于这个原因,他在用户接口上表现为一个随输入生日变化而变化的动态值。
(注:例程中用了UK 格式的时间 - day/month/year (dd/mm/yyyy))
假设用户输入了 11/1/1969 作为出生日期,返回年龄值是33,效果就是在制度栏里显示33,输入9就会报错,而输入1959,数值也就随之变成了43。这个行为可以定义在公民类中:
public event EventHandler OnNewAge;
在公民对象中,当输入生日日期是正确的情况下,就会触发该事件,之后每次合法的改变都会触发该事件已改变年龄栏的值。
客户端处理事件如下:
private void Person_OnNewAge(object sender, System.EventArgs e)
{
// Update the displayed age
lblAge.Text = person.Age.ToString();
}
持久化
持久化通过一个 "manager" 对象实现,PersonManager. 它包括Load, Save and Delete 方法并与公民对象通信。数据通过ADO.NET 与 Microsoft Access database连接。
两者间的接口很简单,在实际环境之,我们需要使用 .Net remoting 技术并把这个类序列化,其原因是PersonManager类通常部署在server上。
在Lhotka 的那本书中,他还阐述了把公民对象分为面向UI的公民对象和面向数据的公民对象两个分别的业务对象,这样更利于进行OR映射。
实例
为了方便说明,示例使用了windows form的用户接口。便于大家理解