我在边界类上有一些事件处理程序,它管理给定泛型事务的持久性机制:
void MyBoundaryClass::MyEventHandler(...)
{
//retrieve stuff from the UI
//...
//declare and initialize trasaction to persist
SimpleTransaction myTransaction(.../*pass down stuff*/);
//do some other checks
//...
//declare transaction persistor
TransactionPersistor myPersistor(myTransaction, .../*pass down connection to DB and other stuff*/);
//persist transaction
try
{
myPersistor.Persist();
}
catch(...)
{
//handle errors
}
}
使用某种TransactionManager来包装SimpleTransaction和TransactionPErsistor对象会更好吗?
是否有任何有用的经验法则可以理解我是否需要更高级别的封装?
目前我遵循的经验法则是“如果方法变得太大 - 做一些事情”。在处理边界事件处理程序时,有时很难在程序和面向对象之间找到适当的平衡。
有什么意见吗?
干杯
答案 0 :(得分:3)
考虑到:
我认为 API 是一个很好的指示,表明新的高级封装的相关性(即新对象的定义)
如果这个新对象提供的服务(即API)是连贯的,并且在重新分组到一个特殊对象时更好地暴露给程序的其余部分,那么无论如何都要使用新对象。
否则,很可能是矫枉过正。
由于您通过创建新对象来展示 公共 API,因此测试的概念在该新对象中可能更容易实现(和其他一些模拟对象),而不是创建许多遗留对象来测试那些相同的操作。
在您的情况下,如果要测试事务,则必须实际测试MyBoundaryClass的MyEventHandler,以便从UI中检索数据。
但是,如果您定义了一个TransactionManager,那么您将有机会降低MyBoundaryClass中存在的不同架构级别的耦合(GUI与数据),并将数据管理导出到专用类中。登记/> 然后,您可以在独立测试场景中测试数据持久性,尤其关注限制值,数据库故障和非标称条件等。
测试场景可以帮助您优化不同对象的内聚(Daok提到的重点)。如果您的测试简单且连贯,那么您的对象可能具有明确定义的服务边界。
因为可以认为Coupling and Cohesion are two cornerstones of OO Programming,所以可以根据它将要执行的一组动作来评估像TransactionManager这样的新类的内聚。
凝聚力意味着某个类执行一组密切相关的操作。另一方面,缺乏凝聚力意味着一个班级正在执行几个不相关的任务。随着越来越多的行为变得分散并最终出现在错误的地方,应用软件最终将变得无法管理。
如果您将在几个不同位置实现的行为重新组合到您的TransactionManager中,它应该没问题,只要它的公共API代表交易所涉及的明确步骤而不是像各种效用函数那样的“关于事务的东西”。一个名称本身不足以判断一个阶级的凝聚力。需要组合名称及其公共API。
例如,TransactionManager的一个有趣方面是完全封装Transaction的概念,即:
答案 1 :(得分:2)
阐述VonC的建议,请考虑以下准则:
如果您希望在其他地方以相同的方式调用相同的函数,则将它们封装在新对象中是合理的。
如果一个函数(或一个对象)提供了一组单独使用的工具,则将其重构为较小的组件是合理的。
VonC关于API的观点是一个很好的试金石:创建有效的接口,对象经常变得明显。
答案 2 :(得分:1)
封装级别应直接链接到对象的内聚。您的对象必须执行单个任务,或者必须分成多个类并封装其所有行为和属性。
经验法则是时候测试你的对象了。如果您正在进行单元测试,并且您意识到您正在测试多个不同的东西(不是在相同的区域操作中),那么您可能会尝试将其拆分。
对于你的情况,我会用你对“TransactionManager”的想法进行封装。这样“TransactionManager”将处理事务的工作方式,而不是“MyBoundaryClass”。