高质量子程序2——高内聚性

1、在子程序层上设计最重要的关注点是什么?

在单个子程序这一层次上,内聚性是设计时常用的启发式方法。对子程序而言,内聚性是指子程序中各种操作之间联系的紧密程度。

有些程序员更喜欢使用“强度(strength)"这一术语:一个子程序中各种操作之间的联系有多强?像cosine()(余弦函数)这样的函数就是极端内聚的,因为整个程序 高内聚,只完成一项功能。而CosineAndTan()(余弦与正切)这个函数的内聚性相对较弱,因为它完成了多于一项的操作。

我们的目标是让每一个子程序只把一件事做好,不再做任何其他事情。

2、内聚性是什么?

内聚性(cohesion)的概念是由Wayne Stevens、Glenford Myers和La仃y Constantine在1974年发表的一篇论文中提出来的。其他一些更为现代的概念,如抽象和封装等,通常在类这一层次的设计中更为适用(事实上,抽象和封装在类层次上己经很大程度上取代了内聚性)。

3、高内聚性的子程序有什么优点?

高内聚性的好处是得到更高的可靠性。

一项针对450个子程序所做的研究发现,高内聚性的子程序中有50%没有任何错误,而低内聚性的子程序中只有18%是没有错误的。

另一项针对另外450个子程序(选取了同样数量的子程序进行研究纯属巧合)所做的研究发现,耦合度与内聚性之比最高的那些子程序,其中所含的错误是耦合度与内聚性之比最低的子程序的7倍之多,而其修正成本则为20倍。

4、什么是好的内聚性?

功能的内聚性,是最强也是最好的一种内聚性。也就是说让一个子程序仅执行一项操作。例如 sin ( ) , GetcustomerName() 、 EraseFi1e( ) 、calculateLoanpayment()以及AgeFromBirthdate()这样的子程序都是高度内聚的。

当然,以这种方式来评估内聚性,前提是子程序所执行的操作与其名字相符一一如果它还做了其他的操作,那么它就不够内聚,同时其命名也有问题。

5、不好的内聚性有哪些?

顺序上的内聚性,是指在子程序内包含有需要按特定顺序执行的操作 , 这些步骤需要共享数据 , 而且只有在全部执行完毕后才完成了一项完整的功能。

举一个顺序上的内聚性的例子, 假设某个子程序需要按照给定出生日期来计算出员工的年龄和退休时间。如果子程序先计算员工的年龄, 再根据他的年龄来计算退休时间, 那么它就具有顺序上的内聚性。

而如果子程序先计算员工的年龄, 然后再重新计算他的退休时间, 两次计算之间只是碰巧使用了相同的出生日期, 那么这个子程序就只具有通信上的内聚性。

你可以创建两个不同的子程序 , 他们能根据给定的生日分别计算员工的年龄和退休时间。其中 ,计算退休时间的子程序可以调用计算年龄的子程序。这样两者就都具有功能上的内聚性了。而其他的子程序则可以调用二者之一或全部。

通信上的内聚性,是指一个子程序中的不同操作使用了同样的数据,但不存在其他任何联系。例如某个子程序先根据传给它的汇总数据打印一份汇总报表,然后再把这些汇总数据重新初始化。这个子程序就具有通信上的内聚性:因为这两项操作只是因为使用了相同的数据才彼此产生联系。

要改善这个子程序的内聚性,应该让重新初始化汇总数据的操作尽可能靠近创建汇总数据的地方,而不是放在打印报表的子程序里。应该把这些子程序进一步拆分成几个独立的子程序:一个负责打印报表,一个负责在靠近创建或修改数据的代码的地方重新初始化数据。然后在原本调用那个具有通信内聚性的子程序的更高层的子程序中调用这两个子程序。

临时的内聚性,是指含有一些因为需要同时执行才放到一起的子程序。

典型的例子有:StartUP()、Shutdown()等。有些程序员认为临时的内聚性是不可取的,因为它们有时与不良的编程实践相关一比如说在Startup() 子程序里塞进一大堆互不相关的代码等。

为避免这个问题,可以把临时内聚性的子程序看做是一系列事件的组织者。前面提到的StartUP()子程序可能需要读取配置文件、初始化临时文件、设置内存管理器,再显示启动画面。

要想使它最有效,应该让原来那个具有临时内聚性的子程序去调用其他的子程序,由这些子程序来完成特定的操作,而不是由它直接执行所有的操作。

过程上的内聚性,是指一个子程序中的操作是按特定的顺序进行的。一个例子是依次获取员工的姓名、住址和电话号码的子程序。这些操作执行的顺序之所以重要,只是因为它和用户按屏幕提示而输入数据的顺序相一致。

另一个子程序用来获取员工的其他数据。这段程序也具有过程上的内聚性,因为它把一组操作赋以特定的顺序,而这些操作并不需要为了除此之外的任何原因而彼此关联。

为了得到更好的内聚性,可以把不同的操作纳入各自的子程序中。让调用的子程序具有单一而完整的功能 。你可能还需要修改用来读取其余数据的子程序。为了让所有的子程序都具有功能上的内聚性,对两个或更多的原有子程序进行修改是很常见的。

逻辑上的内聚性,是指若干操作被放入同一个子程序中,通过传入的控制标志选择执行其中的一项操作。之所以称之为逻辑上的内聚性, 是因为子程序的控制流或所谓 “逻辑" 是将这些操作放到一起的唯一原因。

它们都被包在一个很大的 if 语句或 case 语句中 , 而不是因为各项操作之间有任何逻辑关联。认为是逻辑上的内聚性的标志性属性就是各项操作之间没有关联,因此,似乎更应称其为“缺乏逻辑的内聚性"。

这方面的一个例子是名为 InputAll() 的子程序, 它根据传入的控制标志来决定是输入客户姓名、 员工考勤卡信息, 还是库存数据。这种子程序的主要问题是你不该通过传入控制标志来控制另一个子程序的处理方式。相比之下, 让三个子程序分别完成不同的操作, 要比用一个 “根据传入的控制标志选择执行三项不同的操作之一" 的子程序清晰得多。

如果这些操作中含有一些相同代码或共用了数据, 那么应该把那些代码移入一个低层子程序中,这些子程序也应该包裹在一个类中。

如果子程序里的代码仅由一系列的 if 语句或者 case 语句 , 以及调用其他子程序的语句组成, 那么创建这样一个具有逻辑上的内聚性的子程序通常也是可以的。在这种情况下 , 如果子程序唯一的功能就是发布各种命令 ,其自身并不做任何处理 , 这通常也是一个不错的设计。这类子程序的技术术语便是 “事件处理器 (event handler) " 。

事件处理器通常用在各种交互性的环境中。

巧合的内聚性,是指子程序中的各个操作之间没有任何可以看到的关联。它也可称为 “无内聚性" 或 “混乱的内聚性" 。

很难从巧合的内聚性转变为任何一类更好的内聚性。通常你需要深入地重新设计和重新实现。

6、不好的内聚性会带来什么问题?

一般来说,其他类型的内聚性都是不可取的。它们都会导致代码组织混乱、难于调试、不便修改。

如果一个子程序具有不良的内聚性,那最好还是花功夫重新编写,使其具有更好的内聚性,而不是再去花精力精确地诊断问题所在了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Win_77

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值