K
kidneyball
Unregistered / Unconfirmed
GUEST, unregistred user!
摘自:Patterns of Enterprise Application Architecture
作者:Martin Fowler
2. Layering 请参阅我在 关于:探讨“数据库系统的面向对象开发”
(http://www.delphibbs.com/delphibbs/dispq.asp?LID=946305)中的贴子
3. Organizingdo
main Logic
最简单的储存业务逻辑的方法是使用Transaction Script(事务脚本)。一个Transaction Script
本质上就是一个procedure(过程)。它包括了从界面获得输入信息,对它们进行检验和计
算,把数据保存到数据库,调用外部系统中有用的操作,并且向表达层(用户界面)提供更
多的数据(有可能需要更多的计算来帮助组织和整理回应的数据)。它的基本的组织思想就是
为每一个用户希望完成的动作(Action)提供一个单一的procedure(过程)。因此我们可以把
它看作是动作(或商业事务)的脚本。当然,它在源代码中并不一定就是一个单一的
procedure,它的各部分可能会被拆分成子程序而这些子程序有可能被不同的Transaction
Script公用。但即使这样,最终的驱动力仍然是为每一个单一动作而写的procedure。这样,
一个零售系统可能包括了确定购买、添加商品到购物车、显示分发状态等等的Transaction
Script
Transaction Script的优点在于:
1) 大部分开发者都能理解这种简单的过程化模型
2) 它与一些简单的数据源Layer,例如Row Data Gateway或Table Data Gateway结合得很好。
3) transaction(译注:估计是指数据库中的transaction)的边界非常容易定义:实际事务
开始的时候打开一个transaction,结束的时候就关闭它。这使得后台工具能非常容易地根
据实际情景完成这些操作。
遗憾的是,它同时也有不少的缺点。这些缺点随着业务领域逻辑的复杂性增加而越来越明
显。经常会因为几个Transaction需要做相似的事情,而导致重复代码的出现。虽然这种情
况能通过重构一些公共子程序来稍稍改善,但仍然会有很多重复代码难于移除和难于发现。
这样,应用程序最后将会变成一个没有清晰结构的杂乱的子程序网。
当然,复杂的逻辑正是引入对象的好地方。处理复杂的业务逻辑的面向对象方法是使用
Domain Model。Domain Model的主要组织方法是建立一个由我们的业务领域中的名词所组成
的模型。这样,一个租赁系统就应该有租约、资产等各种类。处理数据检验和计算的逻辑
就应该放到这些Domain Model中去。一个货物分发(shipmeng)对象应该包括计算送货费用的
逻辑。当然,计算帐单子程序仍然有可能存在,但这个procedure应该被一个Domain Model
中的方法代理使用。(译注:Delegate,即不直接调用这个子程序,而通过业务对象的方法
来间接调用。也就是一些朋友所说的“封装”,但我觉得这种说法与encapsulate混淆,因
此倾向于使用“代理”的说法)
使用Domain Model来取代Transaction script,正是现在许多面向对象编程人员津津乐道
的改革的核心问题。这时,每个对象仅包含与自己相关的部分逻辑,而不是一个procedure
囊括了一个用户动作的所有逻辑。如果你不习惯于使用Domain Model,那么学习如何使用
它会非常令人灰心,因为你得在一个又一个的对象之间寻找你想要的行为(译注:behavior,
指一个对象行为的相关程序)。
要用一个例子来说明两者之间根本的不同点是很难的,但在关于模式的讨论中,我会尝试
用两种不同的方法来实现同一个业务逻辑。要看出不同点,最容易的方法是分别看这两种不
同手段的sequence diagram。在这个例子中(http://www.delphibbs.com/delphibbs/dispq.asp?LID=946305),
根本的问题在于不同类型的产品对某个合同会有不同的算法来计算收入。从事计算的method必须
确定某个合同对应什么产品,应用正确的算法,然后创建一个收入对象来保存计算结果。
(为简单起见,我忽略了数据库的交互问题)
Domain Model的价值在于一旦你习惯了它,那么就可以运用很多技术来帮助你有条理地处
理复杂的逻辑。正如在例子中,当越来越多计算收入的算法出现的时候,我们可以简单地
通过加入新的收入策略对象来加入这些算法。如果你使用Transaction Script,我们只能
向脚本中加入更多的条件分支。一旦你的喜好开始象我这样倾向于使用对象,你会发现你
会倾向于选择Domain Model,即使对于一些相当简单的情况也会这样。
使用Domain Model的代价在于它和它对应的数据源Layer的复杂度。对“胖对象模型”不熟
悉的人们需要时间来适应“胖Domain Model”。开发人员常常需要花费几个月的时间参与
使用Domain Model的项目之后,才能把思维模式转变过来。但是,一旦你熟悉了你的第一
个Domain Model,你很可能就会从此迷上它,变得非常倾向于使用它们——这就是象我这
样的对象狂热者的来历了。但是,有少部分的开发人员似乎并不能适应这种转变。
但即使你已经习惯了使用Domain Model,你还必须处理数据映射的问题。你的Domain
Model规模越大,你把它们映射到关系数据的机制(通常是使用Data Mapper)就越复杂。
一个完善的数据源Layer比较象一种固定投资。你付出一定的金钱(如果你购买)或者时间
(如果你自己开发)来得到一个良好的数据源Layer,但一旦你手上有一个,你就能用它做
很多事情。
还有第三种架构业务逻辑的方法:Table Module。初初看去,Table Module和Domain Model很
相似,因为它们都具有关于合同,产品和收入的类(在例子中)。根本的差别在于Domain Model对
数据库的每个合同记录都有一个合同对象实例与之对应,而一个Table Module只有一个对象
来处理所有的合同。Table Module是设计来与Record Set一起工作的。这样,一个合同Table Module
的客户对象会首先向数据库提交一个查询来建立一个Record Set,然后它会创建一个合同对
象并把这个Record Set作为参数传递给它。这样,客户对象就能调用合同对象里的操作来
完成各种事情。如果它希望对某个特定的合同进行操作,则它必须传递一个ID给合同
Table Module对象。
作者:Martin Fowler
2. Layering 请参阅我在 关于:探讨“数据库系统的面向对象开发”
(http://www.delphibbs.com/delphibbs/dispq.asp?LID=946305)中的贴子
3. Organizingdo
main Logic
最简单的储存业务逻辑的方法是使用Transaction Script(事务脚本)。一个Transaction Script
本质上就是一个procedure(过程)。它包括了从界面获得输入信息,对它们进行检验和计
算,把数据保存到数据库,调用外部系统中有用的操作,并且向表达层(用户界面)提供更
多的数据(有可能需要更多的计算来帮助组织和整理回应的数据)。它的基本的组织思想就是
为每一个用户希望完成的动作(Action)提供一个单一的procedure(过程)。因此我们可以把
它看作是动作(或商业事务)的脚本。当然,它在源代码中并不一定就是一个单一的
procedure,它的各部分可能会被拆分成子程序而这些子程序有可能被不同的Transaction
Script公用。但即使这样,最终的驱动力仍然是为每一个单一动作而写的procedure。这样,
一个零售系统可能包括了确定购买、添加商品到购物车、显示分发状态等等的Transaction
Script
Transaction Script的优点在于:
1) 大部分开发者都能理解这种简单的过程化模型
2) 它与一些简单的数据源Layer,例如Row Data Gateway或Table Data Gateway结合得很好。
3) transaction(译注:估计是指数据库中的transaction)的边界非常容易定义:实际事务
开始的时候打开一个transaction,结束的时候就关闭它。这使得后台工具能非常容易地根
据实际情景完成这些操作。
遗憾的是,它同时也有不少的缺点。这些缺点随着业务领域逻辑的复杂性增加而越来越明
显。经常会因为几个Transaction需要做相似的事情,而导致重复代码的出现。虽然这种情
况能通过重构一些公共子程序来稍稍改善,但仍然会有很多重复代码难于移除和难于发现。
这样,应用程序最后将会变成一个没有清晰结构的杂乱的子程序网。
当然,复杂的逻辑正是引入对象的好地方。处理复杂的业务逻辑的面向对象方法是使用
Domain Model。Domain Model的主要组织方法是建立一个由我们的业务领域中的名词所组成
的模型。这样,一个租赁系统就应该有租约、资产等各种类。处理数据检验和计算的逻辑
就应该放到这些Domain Model中去。一个货物分发(shipmeng)对象应该包括计算送货费用的
逻辑。当然,计算帐单子程序仍然有可能存在,但这个procedure应该被一个Domain Model
中的方法代理使用。(译注:Delegate,即不直接调用这个子程序,而通过业务对象的方法
来间接调用。也就是一些朋友所说的“封装”,但我觉得这种说法与encapsulate混淆,因
此倾向于使用“代理”的说法)
使用Domain Model来取代Transaction script,正是现在许多面向对象编程人员津津乐道
的改革的核心问题。这时,每个对象仅包含与自己相关的部分逻辑,而不是一个procedure
囊括了一个用户动作的所有逻辑。如果你不习惯于使用Domain Model,那么学习如何使用
它会非常令人灰心,因为你得在一个又一个的对象之间寻找你想要的行为(译注:behavior,
指一个对象行为的相关程序)。
要用一个例子来说明两者之间根本的不同点是很难的,但在关于模式的讨论中,我会尝试
用两种不同的方法来实现同一个业务逻辑。要看出不同点,最容易的方法是分别看这两种不
同手段的sequence diagram。在这个例子中(http://www.delphibbs.com/delphibbs/dispq.asp?LID=946305),
根本的问题在于不同类型的产品对某个合同会有不同的算法来计算收入。从事计算的method必须
确定某个合同对应什么产品,应用正确的算法,然后创建一个收入对象来保存计算结果。
(为简单起见,我忽略了数据库的交互问题)
Domain Model的价值在于一旦你习惯了它,那么就可以运用很多技术来帮助你有条理地处
理复杂的逻辑。正如在例子中,当越来越多计算收入的算法出现的时候,我们可以简单地
通过加入新的收入策略对象来加入这些算法。如果你使用Transaction Script,我们只能
向脚本中加入更多的条件分支。一旦你的喜好开始象我这样倾向于使用对象,你会发现你
会倾向于选择Domain Model,即使对于一些相当简单的情况也会这样。
使用Domain Model的代价在于它和它对应的数据源Layer的复杂度。对“胖对象模型”不熟
悉的人们需要时间来适应“胖Domain Model”。开发人员常常需要花费几个月的时间参与
使用Domain Model的项目之后,才能把思维模式转变过来。但是,一旦你熟悉了你的第一
个Domain Model,你很可能就会从此迷上它,变得非常倾向于使用它们——这就是象我这
样的对象狂热者的来历了。但是,有少部分的开发人员似乎并不能适应这种转变。
但即使你已经习惯了使用Domain Model,你还必须处理数据映射的问题。你的Domain
Model规模越大,你把它们映射到关系数据的机制(通常是使用Data Mapper)就越复杂。
一个完善的数据源Layer比较象一种固定投资。你付出一定的金钱(如果你购买)或者时间
(如果你自己开发)来得到一个良好的数据源Layer,但一旦你手上有一个,你就能用它做
很多事情。
还有第三种架构业务逻辑的方法:Table Module。初初看去,Table Module和Domain Model很
相似,因为它们都具有关于合同,产品和收入的类(在例子中)。根本的差别在于Domain Model对
数据库的每个合同记录都有一个合同对象实例与之对应,而一个Table Module只有一个对象
来处理所有的合同。Table Module是设计来与Record Set一起工作的。这样,一个合同Table Module
的客户对象会首先向数据库提交一个查询来建立一个Record Set,然后它会创建一个合同对
象并把这个Record Set作为参数传递给它。这样,客户对象就能调用合同对象里的操作来
完成各种事情。如果它希望对某个特定的合同进行操作,则它必须传递一个ID给合同
Table Module对象。