读书笔记

第七章 高质量的子程序 High-Quality Routines
7.1 创建子程序(routines)的正当理由:提高程序管理能力,包括提高可读性、可靠性和可修改性,节省代码空间是次要,或者是副作用(side effect)。
降低复杂度
引入中间,易懂的抽象
避免重复
支持子类化(subclassing)
隐藏顺序
隐藏指针操作
提高可移植性
简化布尔判断
改善性能
确保所有程序都很小
7.2 在子程序层上设计(Design at the Routine Level):抽象和封装理念适合类层次的设计,内聚性适合子程序设计
内聚性(cohesion):指子程序中各种操作之间联系的紧密程度。(一个子程序就干一个活)
功能内聚性:一个子程序只干一件事情
以下是不够理想的内聚性,但是有作用的。
顺序上的内聚性:子程序包含按特定顺序执行的操作,这些操作共享数据,而且只有在操作全部执行完毕的时候才达到一项完整的功能
通信上的内聚性:指一个子程序内的不同操作用同样的数据,但不存在任何的联系
临时的内聚性:一些因为需要同时操作而放在一起的操作
startUp()
以下是不可取的内聚性
过程内聚性:把一组操作放在子程序中并按照特定顺序执行,除此之外没有其他彼此的联系
逻辑上内聚性(其实是缺乏逻辑的内聚性):把若干操作放入同一子程序,通过传入的控制符执行不同的操作
如果子程序仅有由一系列if else语句以及其他子程序语句组成,这样的逻辑上内聚性的子程序是可以的,就是这个子程序只发出各种指令,不进行任何的处理,那么他就是一个事件处理器(event handler)
巧合的内聚性:子程序的操作之间没有任何的 联系
7.3 好的子程序的名字 Good Routine Names
描述子程序所做的所有的事
避免使用无意义、模糊或者表达不清的动词 event handling事件处理除外
handleCalculation()
PerformServices()
OutputUser()
ProcessInput()
DealwithOutPut
不仅仅通过数字形成不同子程序名
outPut1(); outPut2()
根据需求确定子程序的名称长度
函数命名时要对函数的返回值有所描述
printer.isReady()
customerId.next()
给子程序命名时要使用语气强烈的动词加宾语的形式,在面向对象语言中,不用再过程(Procedure)名中不用加入对象名(宾语)
document.print() orderInfo.check()
准确使用对仗词
add/remove insert/delete start/stop up/down
begin/end increment/decrement open/close get/set
first/last old/new min/max show/hide
create/destory lock/unlock source/target get/put
为常用操作确立命名规则
7.4 子程序可以写多长 How long can a Routine be
虽然有很多研究但是仍然没有一个公认的说法,一般是50-200行左右,可以从子程序的内聚性、嵌套层次、变量数量、决策点(desicions points)和注释来决定子程序的长度,当超过200行,就要考虑在可读性上问题了。
7.5 如何使用子程序参数 How to Use Routine Parameters
按照输入-修改-输出的顺序排列参数:暗含了操作数据的顺序
如果子程序用了类似的参数,参数的排列顺序应该一致
使用所有的参数,没有用的要撇除
把状态或者出错变量放在最后,因为它们只是程序的附属
不要把子程序参数当做工作变量
对输入参数进行操作,并将其作为返回值返回结果。
在接口中对参数的假定加以说明,在接口文档说明
参数是仅用于输入,要被修改,还是仅用于输出
表示数量的参数单位(秒/分)
没有用枚举类型的话,应该说明状态码和错误码的含义
所能接受的数据范围
不能接受的特定数值
把子程序的参数个数限制在7个以内
如何一直需要传递很多参数,说明子程序之间的耦合太过紧密。如果需要向很多不同的子程序传入相同的数据,就把这些子程序组成一个类,并把那些经常使用的数据作为类的内部数据
考虑对参数使用某种输入、修改、输出的命名规则
i_xxx,m_xxx,o_xxx 或者 Input_xxx,Modify_xxx,Output_xxx
为子程序传递用以维持其接口抽象的变量和对象
假如经常需要修改子程序的参数表,且都是来自同一个对象,那就传递整个对象
如果只是为传递几个特定的数据,把数据填入对象,再到子程序读取这些数据,那就只传递数据的值
使用具名参数?
确保实际参数与形式参数相匹配?
7.6 使用函数时要特别考虑的问题
函数是有返回值的子程序,过程是指没有返回值的子程序
设置函数的返回值
检查所有可能的返回路径
假如需要返回有关的数据,那就应该作为类的成员保存起来,而不是作为局部数据的引用或者指针返回。
7.7 宏子程序和内联子程序 Macro Routine And Inline Routines***?*
把宏表达式整个都包含在括号内
把包含多条语句的宏用大括号括起来
用给子程序命名的方法给展开后代码形同子程序的宏命名,以便需要可以用子程序来替换宏。
高质量的子程序核对表P185
第八章 防御式编程 Defensive Programming:子程序应该不因传入错误的数据而被破坏,哪怕是由其他子程序产生的错误的数据。即核心思想是程序都是有问题,都是要被修改。
8.1 保护程序免遭非法输入数据破坏 Protecting your program from invalid inputs
检查所有来源于外部数据的值:从文件、网络、用户或者其他外部接口获得的数据应该检查其有效性
检查子程序所有输入参数的值
决定如何处理错误的输入数据
8.2 断言 Assertions
在开发期间使用的,让程序运行时进行自检的代码,一个断言一般包含两个参数,一个布尔表达式,一个断言为假时显示的消息。断言程序执行的前条件和后条件
用断言检查一下假定:
输入参数或者输出参数在预定范围内
子程序开始(结束)文件或者流处于开启或者关闭的状态
子程序开始(结束)文件或者流读写的位置位于开头或者结尾
指针不为空
传入子程序的数组或者其他容器至少能存储X个数据元素
表已经初始化,存储着真实的数据
仅用于输入的变量的值没有被子程序改变
子程序开始或者结束的时候,某个容器为满或者为空
高度优化的子程序与运行缓慢但逻辑清晰的子程序运行结果一致
对于高健壮性的代码,应该先断言再处理错误
8.3 错误处理技术 Erro-Handling Technique:断言处理不应该发生的错误,错误处理技术处理预料中可能发生的错误。
返回中立值
数值计算返回0
字符串操作返回空字符串
指针操作返回空指针等
换用下一个正确的数据
温度计读取数值失败,可以等下一次如1/100秒读取
返回前次相同的数据
上面温度计的例子同样适用
换用最接近的合法值
得到字符串长度小于0,那返回0
把警告信息写到日志
返回一个错误码
调用处理错误的对象或者子程序
健壮性 vs 正确性 robustness vs correctness
高层次设计对错误处理方式的影响
8.4 异常Exceptions:是把错误或者异常事件传递给调用方代码的特殊手段
用异常通知其他的程序,发生了不可忽略的错误
与断言相似,都是用来处理罕见甚至不可能出现的情况???
是处理意外的有效途径,但是增加复杂度
不能用异常来推卸责任,明确是由自身处理异常还是由调用方处理异常
避免在解构函数和构造函数抛出异常,除非在同一地方进行捕获
在恰当的抽象层次抛出异常,就异常应于当前接口的抽象层次一致,避免暴露实现细节和内部信息
避免使用空的catch语句,除非将catch的异常文档化
了解函数库可能抛出的异常、
考虑创造一个集中的异常报告机制,就是对于异常进行统一的格式化并记录和存储
把项目中对异常的使用标准化
可以定义项目特定的异常类,记录日志、报告错误的集中起来和标准化
规定何种场合异常时需要局部处理
规定何种场合异常只能抛出,不能局部处理
考虑异常的替换方案
8.5 隔离程序,使之包容由程序错误造成的伤害Barricade Your Program to Contain the Damage Caused by Errors
隔栏是一种容错的策略 damage containment strategy
可以在类的层面上采用这种方法,在类的公有方法假定输入的数据时不安全的,对数据进行检查并清理,之后再将数据传给私有方法,类的私有方法假定数据都是安全的
隔栏外部的程序使用错误处理技术,在那里对数据的假定是不安全的。内部的程序使用断言技术,这样如果隔栏内的出现错误的数据,就是程序上的错误而非数据上的错误
8.6 辅助调试的代码 Debugging aids
不要把自动地把产品版的限制强加于开发版上:开发版可以花费更多的资源,允许运行缓慢,允许暴露不安全的操作
尽早引入辅助代码
采用攻击式编程 offensive programming
确保断言使程序中止,及时修复错误
case语句的default分支或者else分支产生严重的错误以致不被忽视
计划移除调试辅助的代码
8.7 确定在产品中该保留多少防御式代码 Determine How much Defensive Programming to Leave in Production code
保留那些检查重要错误的代码
去掉检查细微错误的代码
去掉可以导致程序硬性崩溃的代码:虽然便于调试,但是用户体验差
为你的技术支持员记录错误信息
确认留在代码中的错误信息是友好的(friendly):就是要严谨,正式
8.8 对防御式编程采取防御姿态 Being Defensive about Defensive Programing
防御式编程增加复杂度
防御式编程因为要检查参数使程序运行缓慢
核对表 P211

posted @ 2021-11-18 16:07  大风吹爱护  阅读(43)  评论(0编辑  收藏  举报