落魄青年,挨踢民工,已经转行
分类:
2007-10-19 15:06:03
以数据模块为中心的方案
我们需要问一下自己:“为什么要尽量写成基于业务对象的系统?”主要理由有我们需要建立一个分布式的系统,集中化业务规则,增加代码重用。Delphi给了我们高度的生产率,强大的数据库访问能力。充分利用它,我们也能达成上面的三个目标。
Delphi提供了数据模块的机制,允许我们把数据访问和业务规则放置在用户界面之外集中的地方。那么数据模块(TDataModule)是业务对象吗?既是,也不是。问题在于你的界面所围绕的数据模块里面都含有dataset控件,这个dataset已经包含了实体。经常有想设计业务对象的程序员在问:“我怎样才能不连接到一个数据集(dataset)?”概念上,他们意识到一个含有Dataset的datamodule可以作为一个业务对象。在这儿我不会讨论所有可能的问题(在这儿我提议使用datamodeul/dataet的联合体要作为我们的业务对象)。大部分时候他们(指datamodeul/dataet)都是一对一的关系,外表看起来我们的业务对象像是表格,也不存在传统对象具有的复合数据类型。
在delphi中开发业务对象,使用DataModule是一个很好的方案。为什么这样说?因为它充分利用了Delphi工具的力量,享受了OO(面向对象)的好处,却又避免对象和关系数据库映射的痛苦。Delphi开发企业信息系统的优势是可视化整合环境,强大的关系数据库访问能力。使用这个方案让你既不失去任何Delphi本身的优点,又能够使用业务对象的好处。
本次会议的主要议题主要在于使用Delphi已经提供的工具来开发非传统的业务对象。我将演示如何分离UI(用户界面)和数据访问及业务规则,用这种方式,你可以不改动业务对象切换多个不同的UI.我们将演示怎样得到最大化的代码重用和最小化重复编码业务规则。
构造基于业务对象的应用程序
问题域的提出
在这次会议中,我将使用银行和账号作为我们的业务对象来作为一个简单的例子。我挑选这个例子的理由是它能够体现出很多开发基于业务对象的系统的原则。我们将使用Delphi构造这个程序,它能够建立各种不同的能取款和存款的金融账户。
设计Tbank对象
做任何好的设计工作的第一步是要确定你期望达到什么目的。谁是系统的操作者,什么样的规则将实施。这个背景很简单,并且我将通过限制我们的讨论范围在两个主要实体,银行和账户来进一步简化它。我们来建模两种不同的活期账户,它们的基本信息是差不多的,但是存款和取款的规则不一样。
银行系统业务对象
银行,银行是账户的管理者。银行有责任建立新账户,管理账户的访问,比如查一查某个人的账号是多少。
支票账户(Checking Account):这是一种取钱不受约束的账户类型,它不限定每次取钱的上限,也不限定某段时间的交易次数。它还能挂钩一个储蓄账户来透支,这种账户没有最小余额规定。
储蓄账户(Savings Account):这种类型有最小余额限制,并且每月取款不能超过两次。
让我们首先鉴别这两种账户的共性。在多个对象之间发现共性的过程叫泛化。两个账户都必须知道当前的余额,并且都有存取款的功能。在取款方面,它们同样有一些规则要处理。我们观察这些对象,把它们的共性行为放到一个更高层的类别中,称为超类。我们就为这两种账户建立一个超类,就叫账户,把它们共同的方法和属性放进“账户”中。
用例
在面向对象设计的世界里,用例(use cases)非常重要。为了系统正常工作,有一些场景我们必须要解决。很多程序员在这个时候往往会说“给代码我看看”(我有时也会这样!),但是,为了测试我们是否达到了目的,有一个好的测试计划针对企业级系统开发来说是非常重要的。用例就是一个测试场景。
建立新账户,我们必须能够建立新账户。
找出一个账户。我们需要通过一个人的姓名或者身份证号码等信息来找到一个账户,并且提取该账户的相关信息。
存款 我们要能够在账户上存款
取款 不管哪一种类型的交易(签支票,ATM,购物刷卡),基本的作用是一样的。
转账 在支票账户余额不够,打算透支取钱时,将出现从储蓄账户暂借给支票账户的情形
对象设计
我们首先要识别出对象的数据成员和方法,在你定义数据成员时,可能会发现,一些其它的对象必须包含在你的类图(class diagram)中.数据成员和方法描绘出这个对象是怎样的,而如何做到的却是业务对象的私有和保护成员和方法要干的事情。如果你能成功地隔离业务对象的公用接口和实现,你就可以在不影响接口的情况下修改你的实现。在分布式对象环境下,要特别注意不能轻易修改接口,因为你很难确定有哪些应用程序在使用你的业务对象。
用用例来检验对象设计
我们的对象模型必须能够解决那些在用例图中出现的问题,基于对象的系统是被设计成对话式的,这就是说,在对象之间对话讨论的是哪个对象可以完成甚么样的业务功能。我们来讨论一个通过支票账户取款的过程,在这个过程中,需要从其他的透支账户转帐。
方法CanIWithDraw()和getOverdraftAccount()不在我们最初的设计模型中。在一个序列图的调用过程中,为了完成这个任务,我们发现还需要其他的方法。另一个感兴趣的是我们已经说过,对于储蓄账户,我们取款时有频度限制和数目限制。因此在转帐前,储蓄账户同样有CanIWithDraw()方法,来判断此次交易是否被允许。看看下面的序列图,我们发现需要改变原来的对象图(应该是类图,译者)。后面有新的改进过的类图(原始来源没看到)。