松耦合与信息隐藏和易变性

时间:2008-12-12 07:51:20

标签: coupling

我正在阅读Steve McConell编写的Code Complete,我正在考虑他在松散耦合的一节中给出的一个例子。它是关于计算员工假期数量的方法的界面,该方法是根据员工的入职日期及其销售额计算得出的。作者建议a将输入日期和销售额作为方法的参数而不是雇员的实例:

int holidays(Date entryDate, Number sales)

而不是

int holidays(Employee emp)

这个论点是,这解耦了方法的客户端,因为它不需要知道关于Employee类的任何信息。

我想到了两件事:

  1. 提供计算所需的所有参数会破坏封装。它显示了方法的内部结构如何计算结果。

  2. 更难改变,例如当有人决定将员工的年龄也包括在计算中时。人们不得不改变签名。

  3. 你有什么看法?

10 个答案:

答案 0 :(得分:9)

我在第2号参数中看到的问题是

  1. 您假设每个所需的值都来自Employee实例。这绝不是真的。例如,假设您必须考虑公司的财务状况来计算“奖励假期”给予任何员工多少。您是否会将财务状态信息添加到员工类以避免更改签名?

  2. 更改签名不一定“更难”,特别是在如今的工具中,只需点击一下按钮就会突出显示每个调用位置。

  3. 你的参数1的主要问题在于它并没有像其他人所说的那样打破封装。你正在展示什么,而不是如何,封装是什么。

答案 1 :(得分:6)

最终松散coupling获胜。从高耦合到低耦合,有各种类型的耦合:

  
      
  1. 内容耦合(高):一个模块修改或依赖于   另一个模块的内部工作
  2.   
  3. 公共耦合:两个模块共享相同的全局数据(例如全局数据)   变量)。改变共享   资源意味着改变所有   使用它的模块。
  4.   
  5. 外部耦合:两个模块共享外部施加的数据   格式,通信协议或   设备接口。
  6.   
  7. 控制耦合:一个模块控制另一个模块的逻辑   传递有关做什么的信息   (例如,传递一个什么做的标志)。
  8.   
  9. 邮票耦合(数据结构耦合):当模块共享复合数据结构并使用时   只是它的一部分,可能是一个   不同的部分(例如通过整体   记录到只需要的功能   它的一个领域。)。
  10.   
  11. 数据耦合:当模块通过例如参数共享数据时。   每个数据都是一个基本部分,并且   这些是唯一的数据   共享(例如,将整数传递给   计算正方形的函数   根)。
  12.   
  13. 消息耦合(低):模块不依赖于每个模块   另外,他们使用公众   交换参数的接口   消息(或事件,请参阅Message passing)。
  14.   
  15. 无耦合:模块之间根本不进行通信。
  16.   

传入Employee是印记耦合,它比数据耦合更加耦合。如果你真的考虑到易于修改,低耦合效果更好,因为你不必担心不必要的副作用。假设您要更改Employee类的结构。您现在必须检查假期函数的实际实现,以确保它不会破坏数学。

最好的解耦方式是定义一个接口IHolidayable,但这对于这种情况来说太过分了。

答案 2 :(得分:2)

对我而言,“正确”的答案可归结为您在创建此功能时是否定义了高级API或低级API。低级API为任何特定情况提供了方便的灵活性,并为int holidays(Date entryDate, Number sales) 辩护。高级API旨在为客户端尽可能方便地完成一件事。这支持int holidays(Employee emp),因为它在调用者方面需要较少的样板(从Employee类中提取日期和销售额)并且不那么详细。

答案 3 :(得分:1)

1)提供参数不会破坏封装。它只是表明这些参数用于计算假期。 “如何”仍然隐藏在方法中。

2)假日方法不应该是Employee类的一部分。

答案 4 :(得分:1)

  1. 这不破坏封装。打破封装将揭示它用于计算假期的内部方法。提供起始参数是API的定义。

  2. 可以改进API以允许这样的更改 - 但是,大多数方法都表明您应该设计满足当前的要求而不是为不可预见的更改而过度设计(设计变更,但不要尝试预测改变)。最好现在就实现你需要的东西,并在必要时重构。

  3. 在我看来,最好通过尽可能多地解耦和封装来规划变更,以便尽可能轻松地完成重构。试图提前预测每个可能的情况都会导致系统过度设计过度。

答案 5 :(得分:1)

int holidays(Employee emp)

在这种情况下,只有员工才能使用相关功能......

答案 6 :(得分:0)

我想说松耦合的一个主要好处是易于改变。松散耦合的类型可以彼此独立地改变,所以我不理解你在问题中的“vs”。

此外,我不会说你通过为方法提供参数来打破封装。无论如何你都可以实现Sum(int a, int b) - 但你必须告诉我(作为用户)你期望两个数字。

答案 7 :(得分:0)

与其他帖子不同,我同意你的意见,这打破了封装。不是类的封装,而是计算假期的概念。如果你想改变这个计算在未来的工作方式,并且仍然避免紧耦合,我建议将Employee作为一个接口。如果这仍然与Employee紧密耦合,因为您认为可能需要为其他事情计算假期,那么您可以拥有Employee继承的GetsHolidays接口。

当然,您选择的解决方案的参与程度应取决于您认为耦合和封装问题的严重程度。我认为原始解决方案在许多情况下都是可以接受的,如果可能的话,请保持简单。

答案 8 :(得分:0)

  

最好的解耦方法是定义一个接口IHolidayable

这种一揽子陈述真让我烦恼。仅仅因为你使用界面并不意味着你自动解耦任何东西。如果您的员工类实现了计算假期的接口,则调用该方法的任何代码仍然调用相同的方法。在最坏的情况下,调用代码将通过直接访问employee对象而不是IHolidayable接口引用来做到这一点,在这种情况下,你只会使情况变得更糟(因为现在接口和任何类之间现在也有更微妙的耦合实施它。)

接口确实可以帮助解耦,但它们不会自动这样做,即使它们这样做,它们也不一定是比抽象(或其他一些祖先)类更好的解决方案。 / p>

答案 9 :(得分:0)

int holidays (IHolidayInfo obj)

可能是一种方式。在这种情况下,任何实现IHolidayInfo的对象都可以使用“假期”功能。只是一个替代方案。