5

我在管理给定通用事务的持久性机制的边界类上有一些事件处理程序:

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 对象会更好吗?

是否有任何有用的经验法则可以理解我是否需要进一步的封装?

目前我遵循的经验法则是“如果方法变得太大 - 做点什么”。在处理边界事件处理程序时,有时很难在过程和面向对象之间找到正确的平衡。

有什么意见吗?

干杯

4

3 回答 3

3

考虑到:

  • 封装的概念是关于定义一个容器,并且
  • 面向对象的设计基于消息传递(方法的调用)的概念

我认为API很好地表明了新的高级封装的相关性(即新对象的定义)

如果这个新对象提供的服务(即 API)是一致的,并且在重新组合成一个特殊对象时更好地暴露给程序的其余部分,那么一定要使用一个新对象。

否则,很可能是矫枉过正。

由于您通过创建新对象来公开公共API,因此在该新对象(以及一些其他模拟对象)中执行测试的概念可能更容易,而不是创建许多旧对象来测试这些相同的操作。

在您的情况下,如果您想测试事务,您必须实际测试 MyBoundaryClass 的 MyEventHandler,以便从 UI 检索数据。

但是,如果您定义一个 TransactionManager,那么您就有机会降低 MyBoundaryClass 中存在的不同架构级别(GUI 与数据)的耦合,并将数据管理导出到专用类中。
然后,您可以在独立的测试场景中测试数据持久性,特别关注限制值、数据库故障、非标称条件等。

测试场景可以帮助您细化不同对象的内聚力Daok提到的重点)。如果您的测试简单且连贯,那么您的对象很可能具有明确定义的服务边界。

由于可以说耦合和内聚是面向对象编程的两个基石,因此可以根据它将执行的一组操作来评估像 TransactionManager 这样的新类的内聚。

内聚意味着某个类执行一组密切相关的动作。另一方面,缺乏凝聚力意味着一个班级正在执行几个不相关的任务。[...] 随着越来越多的行为变得分散并最终出现在错误的地方,应用软件最终将变得无法管理。

如果您将在几个不同地方实现的行为重新组合到您的 TransactionManager 中,那应该没问题,只要它的公共 API 代表事务涉及的明确步骤,而不是像各种实用程序函数那样的“关于事务的东西”。一个名字本身不足以判断一个类的凝聚力。需要名称及其公共 API 的组合。

例如,TransactionManager 的一个有趣方面是完全封装 Transaction 的概念,它将:

  • 系统的其余部分几乎不知道,并且会降低其他类和“事务”之间的耦合
  • 通过将其 API 集中在事务步骤(如 initTransaction()、persistTransaction()、...)周围来增强 TransactionManager 的内聚性,避免任何 Transaction 实例的任何 getter 或 setter。
于 2008-11-01T18:13:55.710 回答
2

详细阐述 VonC 的建议,请考虑以下准则:

  • 如果您希望以相同的方式在其他地方调用相同的函数,那么将它们封装在一个新对象中是合理的。

  • 如果一个函数(或一个对象)提供了一组单独有用的工具,那么将其重构为更小的组件是合理的。

VonC 关于 API 的观点是一个极好的试金石:创建有效的接口,并且对象通常变得显而易见。

于 2008-11-01T18:43:30.130 回答
1

封装的程度应该与你的对象的内聚度直接相关。您的对象必须执行单个任务或必须划分为多个类并封装其所有行为和属性。

经验法则是何时测试您的对象。如果您正在执行单元测试并且您意识到您正在测试多个不同的事物(不在同一个区域操作中),那么您可能会尝试将其拆分。

对于你的情况,我会用你的“TransactionManager”的想法来封装。这样,“TransactionManager”将处理事务的工作方式,而不是“MyBoundaryClass”。

于 2008-11-01T19:03:00.603 回答