• 代码大全2读书笔记【6-8章】


    摘要
    之前的章节我们了解到构建对软件开发的重要性,接下来这几章我们将讨论构建类和子程序的细节来来达到提升代码质量的目的。

    6-8章节

    6-构建高质量的类

    ADT抽象数据类型

    ADT:描述类的数据和操作(子程序)
    好处:能够隐藏实现细节和内部数据,降低耦合,增加可读性,方便维护(发现错误不用一个个地方修改,只用修改一处,就是这个方法)。

    良好的类接口
    • 类的接口应该展现一致的抽象层次
      根据类就大概能知道类的作用
      在这里插入图片描述

      没有良好抽象的类,各类型混杂的方法职能不专一,会让程序越来越难以理解
      在这里插入图片描述

    • 一定要理解类所实现的抽象是什么
      一些类很相似,需要理解类的接口需要的抽象是哪一个。可能使用的时候我们暂时都可以用,但是一定要仔细考虑。

    • 提供成对的服务
      创建子程序的时候,需要考虑是否需要创建与之互补的另一个操作。

    • 把不相关的信息转移到其他类中。
      可能一个类中两个不同的子程序分别使用到两部分不相交的数据,那么干脆可以将二者拆分成两个类。

    • 尽可能让接口可编程,而不是表达语义
      接口尽量少一些需要人为的在逻辑上需要判断的操作(这类操作必须加注释)。而是尽量拿来就很容易使用,这样再复用或修改代码的时候不容易出问题。

    • 谨防在修改时破坏接口的抽象
      就是后续维护代码,修改代码时若是不严格遵守最初的抽象理念,随意添加不一致的子程序,这样会偏离接口的最初用途,代码变得越来越混乱。

    • 不要添加与接口抽象不一致的公用成员

    良好的封装
    • 尽可能地限制类和成员的可访问性
      避免公开成员数据,少用protected降低派生类和基类间的耦合,在不是很坚定的情况下,尽量选择更严格的访问级别。

    • 避免把私用的实现细节放入类的接口中
      –避免使用友元类
      –要格外警惕从语义上破坏封装性
      –不要对类的使用者做出任何假设
      –不要因为一个子程序里仅仅只用了公用–子程序就将其归入公开接口,而应考虑暴露后展现的抽象是否一致

    • 提高代码可读性比加快编写代码更重要

    有关设计和实现的问题
    • 包含(“has a…” 的关系)
      通过包含来实现"有一个/has a"的关系,如类数据成员
      万不得已时通过private继承来实现,主要原因是让外层的包含类可访问内层被包含类的protected成员
      警惕超过7个数据成员的类,7±2是人们在做其他事情时能记住的离散项目个数

    • 成员函数和数据成员
      让类中子程序数量尽可能减少(减少耦合)
      减少类所调用的不同子程序的数量:研究表明类中错误数量和类所调用不同子程序的总数是相关的。

    总的来说就是减小类和类之间相互合作的范围,尽可能让以下几个数字最小:
    1、所实例化的对象的种类
    2、被实例化对象上直接调用的不同子程序的数量
    3、调用由其他对象返回的对象的子程序数量

    创建类的原因
    • 为抽象的对象建模
    • 降低复杂度
    • 隐藏实现细节
    • 限制变动的影响范围
    • 隐藏全局数据
    • 让参数传递更加顺畅
    • 代码重用性

    避免创建万能类
    消除无关紧要的类
    避免用动词命名的类

    7-高质量的子程序

    什么是低质量的子程序?
    在这里插入图片描述

    如图我们能发现几个问题:
    1、名字很差,handlestuff无法知道这个程序是干嘛的
    2、没有文档说明
    3、布局随意,风格很多,看着很混乱
    4、入参被修改,输入变量值不应该被修改,否则不该叫做inputRec
    5、子程序没有单一目的(既有初始化变量向数据库写数据,又有一些计算操作,彼此没有明显联系)
    6、未防范数据错误,crntQtr若是0则程序会报错(这里java开发很常见,未校验空指针,就直接使用其内部方法)
    7、魔法值
    8、无效代码,有一些未被使用的变量。
    9、入参太多,一般上限书中说是7个(实际开发,个人感觉很少超过5个)
    10、入参顺序混乱,而且没有注释

    构建子程序的正当理由
    • 降低程序复杂度
    • 避免代码重复
    • 隐藏顺序
    • 提高可移植性
    • 简化复杂的布尔判断
    • 更好地改善性能
    • 通过方法名能够提升可读性,甚至达到自我注释的地步
    • 方便重构(维护)
    子程序层上的设计

    内聚性:
    指的是子程序中各种操作联系的紧密程度

    研究表明,拥有大量子程序的代码,低内聚性的代码出错率是高内聚性的7倍

    • 功能上的内聚性:
      即让一个子程序仅执行一项操作

    举例:两个方法,一个cos()一个cosAndTan().
    这两个哪个内聚性强,哪个弱??

    • 顺序上的内聚性(es聚合查询的代码,多项子程序共享数据,所有操作加起来才是一项完整功能)

    • 通信上的内聚性
      指的是多项不同的操作使用了同样的数据(我理解某个类中的全局变量如正则)

    • 临时的内聚性
      比如某些操作需要执行的时机相同,比如启动微服务时的一些操作

    • 过程上的内聚性,(比如银行自助机上注册个人信息,根据提示一步一步,实际上后台代码中每一步并没有关联、数据也互不影响,但可以与操作过程对应,形成过程内聚)

    • 逻辑上的内聚性
      仅仅因为程序的控制流,所谓的逻辑操作(比如if、case)而放到一起的操作成为逻辑上的内聚

    好的子程序名字
    • 描述子程序所做的所有事情
    • 避免使用无意义、模糊或表达不清的动词(比如“handleOutput()”,你根本无法知道它是做什么,“FormatAndPrintOutput()”是不是非常清楚?)
    • 不要仅通过数字来形成不同子程序的名字(outputUser2())
    • 长度最佳范围9-15个字母,命名重点是含义清晰易懂,而不是越短或越长越好
    • 给过程起名时使用语气强烈的动宾形式(saveUser())
    • 准确使用对账词(好的例子:open/close;坏的例子:start/off)
    子程序可以写多长
    • 子程序代码行数推荐(al的编码规范:单行120个字符,子程序不超过80行)
    如何使用子程序参数
    • 按照输入-修改-输出的顺序排列参数
    • 如果好几个子程序都用了类似的参数,应该让这些参数的排列顺序保持一致
    • 使用所有的参数
    • 不要把参数当做工作变量
    • 把子程序参数限制在7个以内
    • 对参数加以描述
    • 使用专门的传输对象(就是dto)

    8-防御式编程

    防御式编程的主要思想:
    不是指编程时抱着防备别人批评和攻击的态度,而是建立一种永远不知道他人将如何使用你的程序的思想。
    我们在编写函数时防范程序因输入错误的数据而遭到破坏。更通俗来说就是要承认程序都会有问题、都需要被修改。聪明的程序员都是带着这种思想编程。

    保护程序免遭无效输入数据的破坏
    • 检查所有源于外部数据的值
    • 检查子程序所有输入参数的值
    • 决定如何处理错误的输入数据
    断言
    • 用异常处理代码来处理预期会发生的状况,用断言来处理绝不应该发生的状况
    • 避免把需要执行的代码放入断言中
      因为关闭断言功能时,容易把有用的代码干掉了
    • 用断言来注解并验证前条件和后条件
    错误处理
    • 返回中立值

    • 换用下一个正确的值

    • 返回与前次相同的数

    • 换用最接近的合法值

    • 把警告信息记录到日志文件中

    • 返回错误码

    • 错误发生时返回错误信息

    • 关闭程序

    • 健壮性和正确性

    正确性意味着永远不返回不正确的结果;
    健壮性意味着要不断尝试采取某些措施,以保证软件继续运行,哪怕程序出点错误。

    高层次设计对错误处理方式的影响

    既然有那么多种方式处理错误,我们就必须注意,应该在整个程序里采用一致的错误处理方法。

    • 在设计决策时就需要确定一套通用的错误处理方法,这是保证程序健壮性和正确性的前提。
    • 防御式编程的重点就是在于防御那些你未曾预料到的错误。
    异常

    异常是把代码中的错误或异常事件传递给调用方代码的一种特殊手段。

    • 只在真正例外的情况下才抛出异常
    • 不能用异常来推卸责任
    • 在恰当的抽象层次抛出异常
      子程序在其接口中展现出一致的抽象,异常也是如此。
      在这里插入图片描述

    在这里插入图片描述

    • 在异常信息中加入关于导致异常发生的全部信息。
    • 避免使用空的catch语句
    • 将项目中使用的异常标准化
    隔离程序,使之包容由错误造成的损害

    在这里插入图片描述

    让软件的某些部分处理“不干净的数据”,另一部分处理"干净"数据,即可让大部分代码无须再担负检查错误数据的职责。

    • 在输入数据时将其转换为恰当的类型
    • 隔离程序外使用错误处理技术,隔离程序内使用断言
    辅助调试的代码
    • 不要自动地将产品版的限制强加于开发版
      开发过程不是真实的用户体验,一些快速运行、节约资源的限制可以先不做要求。比如我们在开发时经常会加一些辅助程序方便调试代码,使开发更顺畅。(比如,一些自动校验数据完整性的代码,每隔几秒会执行一次)
    采用进攻式编程
    • 确保断言让程序终止运行,避免程序员跳过问题,使问题越积累越多
    • 确保每隔case中的default分支或else分支都能产生严重的错误,或者至少让这些错误不被忽视
    • 删除一个对象前,将其填满垃圾数据
    • 让程序把错误日志通过邮件发送给你

    进攻式编程就是主动尝试激发程序的错误,只有开发时经历惨痛的失败,才不会让你发布产品后败的太惨

    确定产品代码中保留多少防范式代码
    • 保留那些检查重要错误的代码
    • 去掉检查细微错误的代码(开发中部分需要调试、排错方便的代码,可以考虑通过版本控制,预编译开关等去掉,不是删掉;或者保留代码,但避免报错,将其不动声色地输出到日志中)
    • 去掉可以导致程序硬性崩溃的代码
    • 记录错误信息
    • 确认留在代码中的错误是友好的
    对防御式编程采取防御态度
    • 过度的防御式编程代码会使程序变得臃肿而缓慢,增加软件的代码复杂度。
    • 在使用防御式编程时需要用辩证的思维,因地制宜的调整优先级。
      在这里插入图片描述
      在这里插入图片描述
  • 相关阅读:
    Java项目:SSM邮件收发管理系统
    java毕业设计乐儿乐社区生鲜团购系统源码+lw文档+mybatis+系统+mysql数据库+调试
    LLM系列 | 27 : 天工大模型Skywork解读及揭露刷榜内幕引发的思考
    Prometheus远程存储方案
    1699. 两人之间的通话次数
    【Kotlin基础系列】第6章 包与导入
    C系统编程:从零手搓一个shell
    基于rt-thread studio的STM32裸机开发第一节:点亮一个LED
    用冒泡排序完成库函数qsort的作用
    基于AI算法+视频监控技术的智慧幼儿园解决方案
  • 原文地址:https://blog.csdn.net/qq_35515283/article/details/125426447