什么时候停止封装?

发布于 2024-07-07 21:43:52 字数 771 浏览 11 评论 0原文

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

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

是否有任何有用的经验法则可以帮助我了解是否需要更高级别的封装?

目前我遵循的经验法则是“如果方法变得太大 - 采取一些措施”。 在处理边界事件处理程序时,有时很难在过程性和面向对象之间找到适当的平衡。

有意见吗?

干杯

I have some event handler on a boundary class that manages a persistence mechanism for a given generic transaction:

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
  }
}

Would it be better to have some kind of TransactionManager to wrap SimpleTransaction and TransactionPErsistor objects?

Is there any useful rule of thumb to understand if I need a further level of encapsulation?

At the moment the rule of thumb I follow is "if the method gets too big - do something about it". It is hard sometimes to find the right balance between procedural and object oriented when dealing with boundary event handlers.

Any opinion?

Cheers

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

征﹌骨岁月お 2024-07-14 21:43:52

考虑到:

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

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

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

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

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

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

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

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

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

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

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

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

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

Considering that:

  • the concept of encapsulation is about defining a container, and
  • object-oriented design is based on the concept of message passing (invocation of methods)

I would argue that the API is a good indication about the pertinence of a new high-level encapsulation (I.e. the definition of a new object)

If the services (i.e the API) offered by this new object are coherent, and are better exposed to the rest of the program when regrouped in one special object, then by all means, use a new object.

Otherwise, it is probable an overkill.

Since you expose a public API by creating a new object, the notion of test may be easier to do within that new object (and a few other mock objects), rather than create many legacy objects in order to test those same operations.

In your case, if you want to test the transaction, you must actually test MyEventHandler of MyBoundaryClass, in order to retrieve data from the UI.

But if you define a TransactionManager, that gives you the opportunity to lower coupling of different architecture levels (GUI vs. data) present in MyBoundaryClass, and to export data management into a dedicated class.
Then, you can test data persistence in independent test scenario, focusing especially on limit values, and database failure, and not-nominal conditions, and so on.

Testing scenario can help you refine the cohesion (great point mentioned by Daok) of your different objects. If your tests are simple and coherent, chances are that your objects have a well-define service boundary.

Since it can be argued that Coupling and Cohesion are two cornerstones of OO Programming, the cohesion of a new class like TransactionManager can be evaluated in term of the set of actions it will perform.

Cohesive means that a certain class performs a set of closely related actions. A lack of cohesion, on the other hand, means that a class is performing several unrelated tasks. [...] the application software will eventually become unmanageable as more and more behaviors become scattered and end up in wrong places.

If you regroup behaviors otherwise implemented in several different places into your TransactionManager, it should be fine, provided that its public API represent clear steps of what a transaction involves and not "stuff about transaction" like various utility functions. A name in itself is not enough to judge the cohesiveness of a class. The combination of the name and its public API is needed.

For instance, one interesting aspect of a TransactionManager would be to completely encapsulate the notion of Transaction, which would :

  • become virtually unkown by the rest f the system, and would lower coupling between the other classes and 'Transaction'
  • reinforce the cohesiveness of TransactionManager by centering its API around transaction steps (like initTransaction(), persistTransaction(), ...), avoiding any getter or setter for any Transaction instance.
旧情别恋 2024-07-14 21:43:52

详细说明 VonC 的建议,请考虑以下准则:

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

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

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

Elaborating on VonC's suggestion, consider the following guidelines:

  • If you expect to invoke the same functions elsewhere, in the same way, it's reasonable to encapsulate them in a new object.

  • If one function (or one object) provides a set of facilities that are useful individually, it's reasonable to refactor it into smaller components.

VonC's point about the API is an excellent litmus test: create effective interfaces, and the objects often become apparent.

把人绕傻吧 2024-07-14 21:43:52

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

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

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

The level of encapsulating should be directly linked to the cohesion of your object. Your object must do a single task or must be divided in multiple class and encapsulate all its behaviors and properties.

A rule of thumb is when it's time to test your object. If you are doing Unit Testing and you realize that you are testing multiple different thing (not in the same area action) than you might try to split it up.

For you case, I would encapsulate with your idea of "TransactionManager". This way the "TransactionManager" will handle how transaction works and not "MyBoundaryClass".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文