代码大全--子程序 读书笔记

 今天主要是看了一下《代码大全》中的关于如何写好一个子过程,这里面主要讲解了子过程的作用,除了避免降低程序复杂性和代码重复的功能外还有其他比较多的作用,这里面有些思想和重构中的思想比较吻合,比如给函数取个好名字,将重复的部分提取出来为函数,为了增加一段代码可读性而提取出函数等等,另外我觉得对耦合和内聚的几种情况讲解的比较清楚,有利于以后写出高内聚,低耦合的程序。
下面是从书中摘抄并整理的一部分:
子程序的作用:
1 降低复杂性:使用子程序的最首要原因是为了降低程序的复杂性,可以使用子程序来隐含 信息,从而使你不必再考虑这些信息。
2 避免代码段重复:无可置疑,生成子程序最普遍的原因是为了避免代码段重复。
3 限制改动带来的影响:由于在独立区域进行改动,因此,由此带来的影响也只限于一个或最多几个区域中。要把最可能改动的区域设计成最容易改动的区域。最可能被改动的区域包括:硬件依赖部分、输入输出部分、复杂的数据结构和商务规则。
4 隐含顺序:把处理事件的非特定顺序隐含起来是一个很好的想法。
5 改进性能:通过使用子程序,可以只在一个地方,而不是同时几个地方优化代码段。把相 同代码段放在子程序中,可以通过优化这一个子程序而使得其余调用这个子程序的子程序全部 受益。把代码段放入子程序也使得用更快的算法或执行更快的语言(如汇编)来改进这段代码 的工作变得容易些。
6 进行集中控制
7 隐含数据结构
8 隐含指针操作
9 隐含全局变量
10 促进重新使用代码段
11 计划开发一个软件族
12 改善某一代码段可读性
13 改善可移植性
14 分隔复杂操作
15 独立非标准语言函数的
16 简化复杂的布尔测试
子程序的命名很重要,如果找到一个合适的命名非常困难,而模棱两可的名字往往代表着某种警告。
内聚与耦合
所谓内聚性指的是在一个子程序中,各种操作之间互相联系的紧密程度,所谓耦合性指的是两个子程序之间联系的紧密程度。耦合性与内聚性是不同的。内聚性是指一个子程序的内部各部分之间的联系程度,而耦合指的是子程序之间的联系程度。研究它们的目的是建立具有内部整体性(强内聚性),而同时与其它子程序之间的联系的直接、可见、松散和灵活的子程序(松散耦合)。
        可取的内聚性:
        功能内聚性:功能内聚性是最强也是最好的一种内聚,当程序执行一项并且仅仅是一项工 作时,就是这种内聚性,这个评价只有在子程序的名称与其实际内容 相符时才成立。如果它们同时还作其它工作,那么它们的内聚性就要低得多而且命名也不恰当。
        顺序内聚性:顺序内聚性是指在子程序内包含需要按特定顺序进行的、逐步分享数据而又不形成一个完整功能的操作。实际上,如果能用一个很典型的动宾词组来命名一个子程序,那么它往往是功能内聚性, 而不是顺序内聚性。给一个顺序内聚性的子程序命名是非常困难的。如果形成了一个完整功能的操作很容易转化为功能内聚性。
通讯内聚性:通讯内聚性是在一个子程序中,两个操作只是使用相同数据,而不存在其它任何联系时产生的。
临时内聚性:因为同时执行的原因才被放入同一个子程序里,这时产生临时内聚性。要避免这个问题,可以把临时内聚性子程序设计成一系列工作的组织者,即形成特定功能的子程序,然后使用该函数去调用各个特定功能的子程序,而不是自己直接来完成相应的功能。
不可取的内聚性:
过程内聚性:当子程序中的操作是按某一特定顺序进行的,就是过程内聚性。与顺序内聚性不同,过程内聚性中的顺序操作使用的并不是相同数据。
逻辑内聚性:当一个子程序中同时含有几个操作,而其中一个操作又被传进来的控制标志所选择时,就产生了逻辑内聚性。之所以称之为逻辑内聚性,是因为这些操作仅仅是因为控制 流,或者说“逻辑”的原因才联系到一起的,它们都被包括在一个很大的 if 或者 case 语句中, 它们之间并没有任何其它逻辑上的联系。但是,如果一个逻辑内聚性的子程序代码都是一系列 if 和 case 语句,并且调用其它子程序, 那么这是允许的。在这种情况下,如果程序的唯一功能是调度命令,而它本身并不进行任何处 理,那么这可以说是一个不错的设计。
偶然内聚性。当同一个子程序中的操作之间无任何联系时,为偶然内聚性。也叫作“无内聚性”。
耦合程度的标准:
耦合规模:所谓耦合规模是指两个子程序之间联系的数量多少。对于耦合来说,联系的数量越少越好,因为一个子程序的接口越少,那么在把它与其它子程序相连接时,所要做的工作也越少。
密切性:密切性指的是两个子程序之间联系的直接程度。联系越直接越好,两个子程序之间联系最密切的是参数表中的参数。
可见性:可见性是指两个子程序之间联系的显著程度。
灵活性:灵活性是指改变两个子程序之间联系的容易程度。
简而言之,如果一个子程序越容易被其它子程序调用,那么它的耦合程度也就越低。这样 的好处是可以增强灵活性和维护性。在建立系统结构时,应该沿着相互耦合程度的最低线将其 分开。如果把程序看成一块木头的话,就是要沿着它的纹理把它劈开。
耦合层次:
简单数据耦合:如果两个子程序之间传递的数据是非结构化的,并且全部都是通过参数表进行的,这通常称作”正常耦合”,这也是一种最好的耦合。
数据结构耦合:如果在两个程序之间传递的数据是结构化的,并且是通过参数表实现传递 的,它们之间就是数据结构耦合的。
控制耦合:如果一个子程序通过传入另一个子程序的数据通知它该作什么,那么这两个子程序就是控制耦合的。控制耦合是令人不快的,因为它往往与逻辑内聚性联在一起,并且,通常都要求调子程序的内容与结构。
全局数据耦合。如果两个子程序使用同一个全局数据,那它就是全局数据耦合的。这也就 是通常所说的“公共耦合”或“全局耦合”。 如果所使用的数据是只读的,那么这种耦合还是可 以忍受的,但是,总的来说,全局耦合是不受欢迎的,因为这时子程序之间的联系既不密切,又不可见。
不合理耦合(pathological)。如果一个子程序使用了另外一个子程序中代码,或者它改变了 其中的局部变量,那么它们就是不合理耦合的。这种耦合也称之为“内容耦合”。这一类耦合是 不能接受的,因为它不满足关于耦合规模、密切性、可见性和灵活性中的任何一条标准。
另外对全局变量的处理写的也很精彩:
全局变量在程序的任何地方都可以进行存取。有时它也被非正式地用来指可存取范围大于局部变量的变量,如在某个单一文件中可以随处存取的模块变量。但是,在单独一个文件中随处可存取,本身并不能表示某一变量是全局的。绝大多数有经验的程序员都认定使用全局变量要比局部变量危险,同时他们也认为利用几个子程序来存取数据是非常有益的。
伴随全局变量的常见问题:
1 对全局数据的疏忽改变。你可以会在某处改变全局变量的值,而在别处又会错误地以为它仍保持着原值,这就是所谓的副作用。
2 伴随全局变量的奇怪的别名问题。“别名”指的是用两个或更多的名称来叫某一变量。当全局变量被传入子程序,然后又被子程序既用作全局变量又用作参数时,就会产生这种问题。
3 有全局数据的代码重入问题。
4 全局数据妨碍重新使用的代码。为了从另一个程序中借用某段代码,首先你要从这个程序中把这段代码取出来,然后把它插入要借用它的程序中。理想的情况是你只需把要用的模块或单个子程序拿出来放入另一个程序中就可以了。
但全局变量的引入则使这一过程变得复杂化了。如果你要借用的模块或子程序使用了全局变量,你就不能简单地把它拿出来再放入另一个新程序了。这时你必须对新程序或旧的代码进行修改以使得它们是相容的。如果想走捷径的话,那最好对旧代码进行修改,使其不再使用全局数据。这样作的好处是下次再要借用这段代码时就非常方便了。如果不这样作的话,那你就需要改动新程序,在其中建立旧有的模块或子程序要用到的全局数据。这时全局变量就像病毒一样,不仅感染了旧程序,而且随着被借用的旧程序中的模块或子程序传播到了新程序中。
5 全局变量会损害模块性和可管理性。开发大于几百行规模软件的一个主要问题便是管理的复杂性,使其成这可管理的唯一办法便是将程序成为几个部分,以便每次只考虑其中的一个部分。模块化便是将程序分为几部分的最有力工具之一。
但是全局数据却降低了你进行模块化的能力。如果使用了全局数据,不能做到每次只集中精力考虑一个子程序吗?当然不能,这时你将不得不同时考虑与它使用了相同全局数据的其余所有子程序,尽管全局数据并没有摧毁程序的模块性,使它减弱了模块性,仅凭这一点就该避免使用它。
全局变量的好处:
1 保存全局数值,有时候需要在整个程序中都要用到某些数据。
2 代替命名常量。
3 方便常用数据的使用。有时候需要非常频繁地使用某一个变量,以至于它几乎出现在每一个子程序的参数表中,与其在每个子程序的参数表中都将这个变量写一次,倒不如使它成为全局变量方便。
4 消除“穿梭”数据。有时候把某个数据传入一个子程序中仅仅是为了使它可以把这一数据传入另一个子程序中,当这个传递链中的某个子程序并没有用到这个数据时,这个数据就被叫做“穿梭数据”。使用全局变量可以消除这一现象。
用存取子程序来代替全局数据,用全局数据能作的一切,都可以通过使用存取子程序来做得更好,存取子程序是建立在抽象数据类型和信息隐蔽的基础上的。即使不愿使用纯粹的抽象数据类型,仍然可以通过使用存取子程序来对数据进行集中控制并减少因改动对程序的影响。
存取子程序的优点
1 可以对数据进行集中控制。如果你以后又找到了更合适的数据结构,那么不必在每一处涉及到数据的地方都进行修改,而只修改存取子程序就可以了,修改的影响可以被限制在存取子程序内部。
2  可以把所有对数据的引用分隔开来,从而防止因其错误造成的影响蔓延。使用类似Stack.array[stack.top]new_element的语句来把元素压入堆栈时,很容易忘记检查堆栈是否溢出从而造成错误。如果使用存取子程序,例如push stack( new_element ),就可以把检查堆栈是否溢出的代码写入push_stack()子程序,这样每次调用子程序都可以对堆栈自动进行检查,而你则可以不必再考虑堆栈溢出问题。
3 你可以自动获得信息隐蔽带来的好处。即使你不是为了信息隐蔽才使用存取子程序的,它也是信息隐蔽的范例性技术之一。你可以改变存取子程序的内部而不必改动程序的其余部分。打个比方,存取子程序使你可以改变房屋的内部陈设而不会变动房屋的外观,这样你仍然可以很容易便找着你的家。
4 存取子程序很容易转换为抽象数据类型。存取子程序的一个优点是:它可以得到很高的抽象级,而直接存取全局变量却难以做到。例如,你可以用存取子程序ifPageFull()来代替语句 if lineCount > Maxlines,虽然这是个很小的收益,但是大量的这类差别便积聚成了高质量软件与东拼西凑到一起的软件之间的不同之处。
怎样使用存取子程序
1  要求所有子程序来对数据进行存取操作。通过存取子程序将数据结构隐含起来。通常需要两个子程序,一个读取数据的值,而另一个用于赋给它新值。除去几个可以直接存取数据的服务性子程序,其它子程序都应通过存取子程序接口来对数据进行存取。
2 不要把所有的全局数据都放入同一个模块中。如果你把所有的全局数据都归成一个大堆,并编写对其存取的子程序,的确可以消除由全局数据带来的问题,但同时也抛掉了信息隐蔽和抽象数据类型所带来的优点。编写存取子程序之前,应先考虑一下每一全局数据应属于哪一个模块,然后把这个全局数据、相应的存取子程序和其关联的子程序放入那个模块中。
3 在存取子程序中建立某种程度的抽象。在数据所代表的意义层次上而不是计算机本身的实现细节层次上建立存取子程序,可以使你更容易应付可能的变动。
4 把对数据的所有存取保持在同一抽象水平上。如果你用了某一存取子程序对某一数据进行了一项操作,那么对这一数据的其它操作也应通过存取子程序来实现。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值