什么时候停止封装?

Joh*_*dol 5 oop encapsulation

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

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
  }
}
Run Code Online (Sandbox Code Playgroud)

使用某种TransactionManager来包装SimpleTransaction和TransactionPErsistor对象会更好吗?

如果我需要更高级别的封装,是否有任何有用的经验法则可以理解?

在我遵循的经验法则的那一刻,"如果方法变得太大 - 做一些事情".在处理边界事件处理程序时,有时很难在过程和面向对象之间找到适当的平衡.

任何意见?

干杯

Von*_*onC 3

考虑到:

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

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

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

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

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

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

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

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

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

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

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

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

  • 系统的其余部分几乎不知道,并且会降低其他类和“事务”之间的耦合
  • 通过将其 API 集中在事务步骤(如 initTransaction()、persistTransaction() 等)周围,避免任何 Transaction 实例使用任何 getter 或 setter,从而增强 TransactionManager 的内聚性。