今天软件构造考试结束了,这门课真的上的听玄幻的
主要通过对ppt的整理得到的笔记
格式是word里的格式,有原件和ppt,但是不方便直接发 有重构的书pdf
软件构造
前言
l 推荐书目 代码大全 代码整洁之道 重构改善既有代码设计
l 主要都是ppt里的 合肥工业大学张高峰
l
目录
软件构造... 1
前言... 1
目录... 1
软件构造是个啥... 1
高质量代码... 4
变量... 20
语句... 30
代码改善... 37
系统考虑... 69
软件工艺... 79
测试驱动... 79
《编程珠玑》... 80
软件构造是个啥
l 软件构造和别的课的关系
n 软件构造与软件工程
– 作文写作课和思政课、历史课等
n 软件构造与数据结构
– 作文写作课与成语课
n 软件构造与设计模式
– 作文写作课与“八股文”写作课
l 软件构造目标
n 提高“代码质量”
n 与“软件质量保证与测试”的异同
n 正确性,可读性,鲁棒性,利于协作和维护等。。。
n ,
n 重要性
u – 软件开发的主要组成部分
u – 软件开发中的核心环节
u – 对着整个软件开发的效率和成果具有决定性影响
u – 必不可少的
n 软件构造是软件开发的中间环节,所以它的前期 至关重要
n 需求,可行性,设计,(),测试,维护等
n (里面就是软件构造的位置)
l 软甲类型
n 商业系统,安全关键系统(mission-critical),嵌入式 安全关键系统(life-critical)
n ,
l 顺序开发与迭代开发
n
n 顺序开发按顺序 迭代开发几乎同时进行
n ,
n 顺序开发
u • 需求稳定
u • 设计准确清晰
u • 开发团队了解这一应用领域
u • 项目风险小
u • 长期可预测
u • 后期改变需求、设计和编码的代价昂贵
n 迭代开发
u • 需求并未理解透彻,不稳定
u • 设计复杂或具有挑战性,或两者兼备
u • 开发团队不熟悉这一应用领域
u • 项目风险多
u • 长期可预测性差
u • 后期改变需求、设计和编码的代价可能较低
l 前期准备
n 问题定义
u 描述的是问题,而不是方案(确定瞄准了目标)
n 需求
u 软件应该做什么,明确正式
u 软件规格(需求)说明书
u 需求是稳定的?
u 需求变更
u P42
n 架构
u 使用合理的解决方案
u – 程序组织,program organization
u – 主要类,major classes
u – 数据设计,data design
u – 业务规则,business rules
u – 用户界面设计,UI design
u – 资源管理,resource management
u – 安全,security
u – 性能,performance
u – 可伸缩性,scalability
u – 互用性,interoperability
u – 国际化/本地化,internationalisation/localisation
u – 输入输出,input/output
u – 错误处理,error processing
u – 容错性,fault tolerance
u – 架构可行性,architectural feasibility
u – 过度工程,overengineering
u – 成熟产品的购买,buy VS build
u – 复用,reuse
u – 变更策略,change strategy
u – 架构质量,quality
u – p54
l 构建决策
n • 软件构建决策,software construction decision
u 编程语言,language
l – 语言的表达能力
l – 语言的表达风格
u 程序约定
l 命名、机构等
u 技术浪潮
u 构建的主要工作
l P69
n 构建的主要准备工作
u 编码
u 团队工作
l 合作方式和流程
u 质量保证
l 测试准备
u 工具
l 版本语言等
高质量代码
l 软件构造中的设计
n 软件设计与构造边界不明显
n ,
n 软件设计中的挑战
u – 危险和试错
u – 混乱的过程和“整洁”的结果
u – 妥协和权衡
u – 带着镣铐跳舞
u – 不确定
u – 启发式
u – 演进的
l 软件构建中的设计
n 管理复杂度
u 首要技术使命
u 本质性和偶然性
u 大O表示法
u 计算复杂度和算法分析
u 复杂的问题,复杂的方法,错误的方法
n 好的设计是啥样的
u • 最小复杂度
u • 易于维护
u • 松散耦合
u • 可扩展性
u • 可重用性
u • 高扇入
l 扇入是指直接调用该模块的上级的个数,扇入高表示模块的复用度高
l
u • 低扇出
l 扇出指一个模块直接调用下级的个数,扇出大一般表示模块的复杂度高,但是扇出过小也不好。
l 扇出过大一般是因为缺乏中间层次,应该适当增加中间层次的模块。扇出太小时可以把下级模块进一步分解成若干个子功能模块,或者合并到它的上级模块中去。
u 可移植性
u 精简性
u 层次性
u 标准技术
n 设计层次
u 软件系统
u 子系统或包
u 类
u 子程序
u 子程序内部
n 如何设计
u 启发式(试探法)
u 面向对象设计(find real-world objects)
l • 确定对象和属性
l • 确定针对的对象的操作和对象间操作
l • 确定对象的可见部分
l • 确定对象的接口
u 例子
l 1.确定对象和属性:一个计费系统,四个类
l
l 2.确定操作:methods and relations
l
l 3.确定可见部分:private and public
l
l 4.确定接口:interface
l
u 抽象
l 概念的聚合
l 对象的基类
u 封装
l 和抽象的关系
l • 广义的封装,虚拟机,容器化等
l
l 不给使用者看里面的原理
u 继承
l 继承的方式
l 继承的时机
l 继承与多态
u 信息隐藏
l 结构化和面向对象程序设计的基础,它引出了“封装”和“模块化”,并紧 密联系“抽象”
l 隐藏复杂度
l 隐藏变化源
l ,
l 例子
n 常数
n 循环依赖
n 变量作用域
u 关键区域的部分,易变化的
l 业务规则
l 硬件依赖部分
l 输入与输出
l 非标准语言特征
l 隔离
l 状态变量
l 数据量的限制
u 松散耦合
l 如何衡量:规模,可见性,灵活性
l 耦合类型
n • 简单数据参数耦合,simple-data-parameter coupling
n • 简单对象耦合, simple-object coupling
n • 对象参数耦合, object-parameter coupling
n • 语义耦合, semantic coupling
u 设计模式
l
l 程序设计与体系架构课里面的
u – 高内聚
l – 分层结构
l – 严格执行规则
l – 分配功能和职责
l – 测试优先
l – 避免错误
l – 时间敏感
l – 中央控制点
l – 蛮力
l – 画图
l – 模块化
u 设计实践(关注设计本身 -> 关注设计的过程)
l
l
l
l 原型系统
n • 实验性
n • 模拟
l 协作
l 设计的深入程度
n 设计与构造的边界
l 设计文档与设计过程文档
n • 敏捷开发
n • Wiki
n • UML,挂图,思维导图,etc
l • 抽象数据类型和类
l • 类接口, class interfaces
l
l ?
n • 类接口
u – 类接口应具有统一的抽象层次
u – 抽象的准确内容
u – 成对的功能设计
u – 不相关的内容放到其它类
u – 接口可编程,减少语义依赖
u – 代码维护中谨慎处理抽象
u – 同时考虑抽象性和内聚性
u – 。。。
n
n ?
n 类接口
u – 类接口应具有统一的抽象层次
u 抽象的准确内容
l 慎重挑选抽象结果
u 成对的功能设计
l Get and set函数
u – 不相关的内容放到其它类
l 例如某个类中 一半子程序使 用一半数据, 另一半子程序 使用另一半数 据,分拆它
l 拥有不同内容的类分离
u – 接口可编程,减少语义依赖
l 接口语义是指“接 口如何使用”,例 如调用先后顺序, 接口使用的前提条 件等。
l 可通过注释说明, 但尽可能减少对其 的依赖
u – 代码维护中谨慎处理抽象
l 类接口和及其抽象表现
l
u – 同时考虑抽象性和内聚性
u – 。。。
n • 类接口和封装
u – 限制类和成员的可见性
l 能用private不 用protected, 能用protected 不用public
u – 不要暴露成员数据
l Gets and set,用函数不要直接用数据
u – 不要在接口中体现私有的实现细节
l
l ?
u – 不要限制类的使用者们
l
l ?
u – 一个方法是否作为公共接口不取决于其是否使用公用方法
l 决定一个子程序是否为 接口由其自身决定,而 不是它所依赖的子程序
u – 避免使用友元类
l 能不用就不用
l 友元(Friend)是C++中的一个特殊机制,它可以实现在某些情况下,一个类的私有成员可以被其他类或者函数访问,从而保证代码的灵活度和可维护性。
l 但是会 破坏C++的封装特性,友元机制容易滥用,友元机制可能导致代码耦合性增加
u – 代码可读性
l 好代码不用注释
u – 不要破坏语义上的封装性
l
l 我知道,我知道个屁
l ?
l 你不是在针对接口编程,而是在针对类实现细节编程,封装性破坏无余!
u – 注意比较紧密的耦合关系
l 类
n • 类内部设计与实现
u 包含关系
l 面向对象编程的主力技术
l “private继承”需要慎重使用
l 包含过多值得警惕
u 继承关系:
l 类之间的共用
l 优先使用“public继承”
l 继承增加复杂度,仔细使用
l Liskov替换原则(LSP):派生类必须能通过基类的接口使用,且 使用者无需了解其差异。(里氏替换原则)
n 基类Account,和三个派生类CheckingAccount, SavingAccount, AutoLoanAccount,程序员可以能调用三个派生类中的从Account 继承来的每一个子程序,而无需关心具体哪个类的对象。
l 只继承需要继承的部分:三种基本情况
n
l 不要覆盖一个不可覆盖的成员函数(private继承):派生类中的 函数不要与基类的重名(重名就不知道调用的是哪个了,容易让人造成误解)
l 共用的接口、数据和操作在继承体系的位置尽可能高
l 只有一个实例的类?除非是单例模式(也不怎么用这个模式)
l 只有一个派生类的基类?
n So?
l 避免继承体系过深
l 派生后的覆盖了一个函数,但是没有操作。
n
n ? 想说啥
l 数据和属性尽可能私有,以免破坏封装性
l – 多态是个好东西
n 画图,画圆圈,画正方形,画三角形。。。。。。
u • 多重继承
l 慎之又慎!
l 菱形继承
l
l
u • 成员函数与数据成员
l 成员函数的数目不宜太多
l 少调用外部函数、
l 调用不宜过深
n ? 啥样是深调用
l – 类与类之间的协作范围尽可能小
u • 构造函数
l – 在所有的构造函数中初始化所有的数据成员(防御式编程)
l – 用私用构造函数中实现单例
n
n 不能从外部直接调用了
l 深副本和浅副本
n 别称 深拷贝和浅拷贝
u 引用对象和值对象(浅拷贝就是只进行值拷贝,直接用
u )
u 深拷贝 就是通过自定义,拷贝构造函数,完成深拷贝动作,能够将数值和地址都拷贝进去。
u
u 类的创建
l – 映射真实世界
l – 映射抽象的对象
l – 降低复杂度
l – 隔离复杂度
l – 隐藏实现细节
l – 限制变化的范围
l – 隐藏全局数据
l – 传递参数
l – 中心控制点
l – 易于重用
l – 程序族和系列软件
l – 操作整合
l – 重构
u 类的设计原则
l 让类尽可能小
l – 单一职责原则(SRP)
l – 开闭原则(OCP):对扩展开放,对修改关闭
l – 里氏替换原则:说法和定义众多,注意区分
l – 接口隔离原则(ISP)
l – 无环依赖原则 避免出现 两个类互相依赖,比如公司类包含员工信息,员工类包含公司信息。
n
n 隐式包含
n
n 函数里调用对象
l – 依赖倒置原则(DIP):解决模块依赖
n
l – 迪米特法则:不要和陌生人说话 (每个实体尽可能少于其他实体发生联系)
l 方法
n 从类到函数 找问题
n
n 函数的动机
u – 降低复杂度
u – 中间级的抽象(类高级抽象)
u – 避免代码重复
l
u – 子类化
u – 隐藏顺序
u – 隐藏指针
u – 可移植性
u – 简化布尔判断
u – 改善性能
u
u 注 小的函数有时也是很好的
n 函数设计 内聚性越高越好
u 功能上的内聚性(功能单一)
u 顺序内聚性(数据依赖和过程顺序)
l • E.g. 两个功能内聚的函数顺序在一起,且数据依赖
u 通讯内聚性(数据依赖)
l • E.g. 两个功能内聚的函数操作相同数据(参数相同)
u 时间内聚性(时间依赖)
l • E.g. Startup()中放了一些没有功能依赖的代码
l ?
u 过程内聚性(过程顺序)
l • E.g. 两个功能内聚的函数顺序在一起,但无数据依赖
u 逻辑内聚性(包含大选择语句)
l • E.g. saveALL(), inputALL(), etc., 包含若干互斥选择的操作(逻辑上分离了)
u – 巧合内聚
l E.g. 一切只是个意外~~
n • 函数命名
u 描述函数全部功能
u 避免无用的动词 (handlestuff(),etc.)
u 数字在名字中少有意义
u 名字的长度
u 体现返回值
u 对应关系,成对出现 get set
u 习惯命名
u
n 函数长度
u 1个屏幕或1-2页纸, 50-150行(最好)
n 函数参数(函数接口是最容易出错的地方)
u – 顺序(输入-修改-输出,相似函数间的一致性,状态和出错变量在最后)
u – 使用所有的参数
u – 个数,7?
l 多了 不要超过七个
u – 命名规则,i_, o_, m_, 或input_, output_, modify_
u – 不要把参数作为工作变量
l
u – 使用断言(assert)来检查参数输入
l 使用assert语句进行输入验证:在函数或方法的开头,可以使用assert语句来验证输入参数的合法性,例如检查参数的类型、取值范围等。这样可以在早期发现错误的输入,提高代码的健壮性。
l assert语句用于在代码中进行断言检查,用于确保某个条件为真。如果断言条件为假,则会触发AssertionError异常。
u – 维持函数抽象很重要(类似于类接口的抽象要求)
n 函数与过程
u 有明确的的返回值就是函数
u 用函数返回值返回运行状态 = 过程
u 统一和一致
u ,
u 设置函数返回值
l 检查所有执行路径
l ?
l 不要返回局部数据的指针(保护数据)
n 宏函数和内联函数
u 内联函数是在编译时展开,而宏在预编译时展开;在编译的时候,内联函数直接被嵌入到目标代码中去,而宏只是一个简单的文本替换。
u 内联函数可以进行诸如类型安全检查、语句是否正确等编译功能,宏不具有这样的功能;宏不是函数,而inline是函数。
u 宏在定义时要小心处理宏参数,一般用括号括起来,否则容易出现二义性。而内联函数不会出现二义性。
u inline有点类似于宏定义,但是它和宏定义不同的是,宏定义只是简单的文本替换,是在预编译阶段进行的。而inline的引入正是为了取消这种复杂的宏定义的。
u 用内联不用宏
n 不要相信程序使用者 不要相信你的队友/同事/对手/…,也不要高估你自己!
n 有问题的输入数据
u 数据值的检查
u 检查函数的输入参数
u 错误数据的处理
u ,
n 防御式编程
u 断言,assertions(见上面)
u 使用场景
l – 参数的取值范围在预计范围中
l – 文件或流处于打开状态与否
l – 文件或流的读写位置是否处于开头
l – 文件或流是否可写
l – 变量没有被意外修改
l – 指针非空
l – 对象是否初始化
l – 容器是空或满
l – 运算结果一致
l – …
u 指导原则
l 用断言处理不应该发生的状况,用错误处理代码处理预期会发生 的状况
l 不要将需要执行的代码放到断言中(不会执行,只会报错)
l 用断言来注解并验证前条件和后条件
l 高健壮性的代码,应先使用断言再使用错误处理代码
n 错误处理技术,
u 处理可能发生的错误
l – 返回中立值
l – 选用最接近的合法值
l – 加入日志文件
l – 返回一个错误码(类似断言了)
l – 换用下一个正确数据
l – 调用专门的错误处理函数或对象
l – 显示出错信息
l – 局部压制
l – 关闭程序
u 正确性和健壮性的选择和平衡
u 错误处理手法的统一规范
n 异常,exception
u “我不知道该怎么处理,那就交给系统来处理吧”
u
u 6666666
u 使用异常时的一些注意事项
l – 用异常告诉其它代码:这里有不可忽略的错误
l 异常与断言类似,是用于处理不应该发生的状况
l 不要用异常来推卸责任(异常不是垃圾桶!)
l 不要在构造函数和析构函数中抛出异常(这是设计专门的初始化 函数的目的之一)
l 在恰当的抽象层次抛出异常
n ? 比如?
l 在异常消息中加入导致异常发生的全部信息
l 不要使用空的catch语句
l 了解所使用的函数库抛出的异常
l 创建一个集中的异常报告机制
l 异常使用的标准化
l 考虑异常的替换方案
n 隔离,barricade
u 容损策略,类似于沙箱,隔离区,DMZ等
u
u 洗干净
u
n 代码调试
u 开发版与产品发布版,debug和release
u 尽早加入调试代码
u 攻击性编程,offensive programming
l • 大胆暴露,惨痛失败,极限考验。(这么带劲的么?)
u 有计划的移除调试代码
l 版本控制工具
l 内置预处理器
l 写自己的预处理器
n 防御式编程的扫尾
u – 保留检查重要错误的代码
u – 去除检查细微错误的代码
u – 去除导致程序硬性崩溃的代码
u – 保留可以让程序稳妥崩溃的代码
u – 为技术支持人员和维护人员记录错误信息
u – 确认错误信息是友好的
n 防御式编程不是万能的
n P211
语句
l 线性代码结构
n 简单而不简单
n 语句之间有前后依赖关系
n 语句之间没有依赖关系
n
n 单纯的顺序排列
l 线性代码有前后依赖关系
n 语句间显式依赖关系
n 语句间隐式依赖关系
n – 尽可能让依赖关系变明显
n – 注意使用合适的变量名、函数名、函数参数突出依赖关系
n – 用注释说明依赖关系
n – 用断言和错误处理代码来检查依赖关系
n ,
l 以程序易于自上而下阅读----一般原则
n
l 线性代码没有依赖关系
n – 以程序易于自上而下阅读----一般原则
n – 相关的语句放在一起:处理了相同的数据,执行了相同的任务
n
l 分支语句
n if语句
n case语句
n \
n ,
n if语句
u – if-then语句+if-then-else语句
u – 先写正常处理代码(then),再处理不常见情况(else)(把可能性大的放前面,可能性小的放else里面)
u 不要混淆“<”和“<=”,“>”和“>=”,“off-by-one”错误
u if 的then分支不要空着
u 认真思考else子句是否需要
u 两个子句不要反了
u if-then-else语句串
l 利用布尔函数简化复杂的检测
l 最常见的情况放在最前面
l 确保所有情况都考虑到了
l 确保所有情况都考虑到了
n case语句
u 各个子句的顺序
l – 按照重要性从先向后
l – 按照字母或数字顺序
l – 正常情况在前,少见情况在后
l – 执行频率高的在前,低的在后
u 注意事项
l – 各个子句的操作尽可能简化
l – default语句用于检查真正的默认情况
l – default语句用于错误检测
l – C类语言每个子句需要注意break语句的使用
l 循环
n – 计数循环
n – 连续求值循环
n – 无限循环
n – 迭代器循环
n – 灵活度和检查位置
n – 固定循环次数,for循环
n – 不固定循环次数,while循环,do while循环
n – 遍历容器循环,foreach
n ,
n 循环控制
u 循环出错主要是围绕循环控制条件展开
u 进入循环
l • 单一循环入口
l • 初始化代码紧放在循环体之前
l • while(true),for(;;)无限循环
l • 可用while可用for,优先用for(利于集中控制)
l • 适合用while的情况不要用for
u 执行循环体
l 用括号把循环体抱进来,加强可读性
l 避免空循环(不报错但是多余)
l 循环内务操作要么在循环开始,要么在结束,j++,i=i+1
l 一个循环只做一件事
u 退出循环
l • 确认循环能够终止,尤其是在异常情况下
l • 使终止条件明显(一眼就能看到那种)
l • 不要为了终止for循环而改变循环下标(逻辑错误)
l • 不要依赖循环结束以后的循环下标取值
l • 使用安全计数器(while循环,do while循环)
u 提前退出
l • while中使用break
l • 如果break很多,要特别小心
l • 在循环开始处使用continue
l • Java中break可以带标号
l • break和continue都需要小心使用
u 检查端点
l • 开始情况,中间情况,结束情况
u 使用循环变量
l 用整数或枚举表示数组和循环的边界
l 在嵌套循环中使用有意义的变量名来提高可读性
l 用有意义的名字来避免循环下标混淆
l 循环下标变量的作用域限定于本循环内
u – 循环的长度
l • 尽可能的短,一目了然,15?20?50?
l • 循环嵌套限制在3层以内(逻辑上太复杂看懂浪费时间)
l • 长循环的内容可以写到函数里
l • 长循环保持清晰,单入单出
n 从内向外生成循环
u 先设计循环体,再设计循环控制条件,一层一层向外
u 例子很简单,但是思路很重要
u .
l 特殊控制结构方法
n 特殊不代表少,也不代表没有,只是代表有争议
n 函数中多处返回
n 递归
n Goto
n 观点
n .
n ,
n ,
n 函数中多处返回
u 需要使用return的时候不要犹豫
u 可以作为防御式编程的一种手段
u 不可滥用
n 递归 recursion
u 递归很有用
u 使用需要谨慎
u 确认递归可以停止(不能逻辑上无限循环)
u 使用安全计数器防止无限递归
u 递归限定在一个函数里(封装递归)
u 小心栈结构
u 递归不是一个追求性能的方法(追求逻辑的实现简单化,电脑理解更麻烦更慢性能更差)
n goto语句
u – 不鼓励使用goto,尤其是在商业开发中
u – 牛人除外
u – 17.3这一节值得精读,反复体会。
l 表驱动法
n 编程模式,scheme:
u 在表里查找数据,而不是使用逻辑 语句(if,case)
n 访问和管理大量规整数据,以表的形式来组织数据,针对 复杂的逻辑链将非常有用
n 直接/间接
n 索引表,阶梯访问表
n
l 控制上的注意事项
n 程序执行中的控制
u 结构化编程
l – 布尔表达式
n 使用true&false而不是0&1来做判断!!!
u 隐式比较布尔值和true&false
n – 简化复杂的表达式
u 拆分复杂的判断并引入新的布尔变量
u 把复杂的表达式做成布尔函数
u 用决策表代替复杂的条件
n 肯定形式的布尔表达式
u 人的认知习惯都是从肯定开始
u if语句中,判断条件以肯定形式为佳
u • 德.摩根定律(不肯定的也可以变肯定)
n 用括号使布尔表达式清晰
n 注意不同语言编译器对复杂布尔表达式的求值过程
u 短路&惰性
u
u ?
n 按照数轴顺序写数值表达式
u
u 用布尔分隔数轴
n – 如何与0比较
u ? 这块看不太懂
u • 隐式比较逻辑变量
u • 把数与0比较
u • 在C中显式比较字符和零终止符(’\0’)
l 本质来说,NULL,0,'\0'都是一样的,都是值0。是的,你没有听错。说到这本文差不多应该结束了。不过为了不被打,还是继续说一说。它们虽然值都是0,但是含义却是不一样的。
n NULL
n 虽然值是0,但是它的含义不一样,或者说它的类型不一样。NULL是指针类型,不过它是空指针,即值为0
n '\0'
n 我们都知道\是转义符,用单引号包起来,再加转义,实际上就是0,只不过它表示的是字符。就向下面这样:
n "0"
n 用双引号包裹的0是字符串,我们看不到的是它结尾还有一个’\0‘
u • 把指针与NULL相比较
u • 其他一些规则
u • C家族中,常量在比较的左端
u • C++中可以使用预处理宏替换&&,||和==
l 虽然可以使用宏来替换这些运算符,但这样做可能会使代码变得难以理解和维护
u • 在Java中,a==b和a.equals(b)的区别
l “a==b”和”a.equals(b)”有什么区别?
l 答:
l 基本类型:==是对比值大小。
l 引入类型:==对比地址是否相等。
l 如果a 和 b 都是对象,则 前者是比较两个对象的引用,只有当 a 和 b 指向的是堆中的同一个对象才会返回 true,而 a.equals(b) 是进行逻辑比较,当内容相同时,返回true。注意a.equals(b)中a和b都是String类型;如果String和char比较可以把char转换为String。举例:char b=‘A’;String a=b+"";
l – 复合语句
n 括号一起写出
n – 用括号把条件表达清楚
n
l – 空语句
n ? 有啥用?
n 小心使用
n 可使用预处理宏或内联函数来取代空语句
u 预处理宏或内联函数通常用于生成代码块或其他需要特殊处理的文本。
u ?搞不懂啥意思
n 考虑一个非空的循环体,会让代码更清晰
l – 深层嵌套
n – 通过重复检测条件中的一部分来简化嵌套的if语句
n – 用break来简化嵌套if
n – 把嵌套if转换成if-then-else语句
n – 把嵌套if转换成case语句
n – 把深层嵌套的代码组成单独的函数
n – 使用多态继承的方法来化简
n – 使用状态变量重写
u 状态变量是一种用于描述系统或程序运行过程中不同阶段状态的变量。它可以在程序运行过程中发生变化,以反映系统的当前状态。
n – 使用异常处理和断言
u 还是差不多麻烦感觉
n – 完全重新设计
l 结构化编程
n 使用顺序、选择和迭代三种结构可以实现任何一种控制流
l 控制结构与复杂度
n 控制复杂度,取决于控制流的复杂度
n 算法复杂度,大O表示法
n
变量
l 数据认知和数据结构
l 变量声明
n – 显式声明和隐式声明
u 显示声明 是程序中的一条说明语句,它列出一批变量名并指明这些变量的类型
l 如C、C++、Java
l // 显示声明 变量count,类型为int,占用空间大小各语言可能有所不同
l int count;
u 隐式声明 指通过某种默认协定的方法将变量名与类型绑定
u
u 如在Fortain中,一个以字母I、J、K、L、M或者N(或其对应小写)开始,它们被隐式的声明为Integer类型。否则为Real类型。
n – 声明全部变量
n – 命名规则
n – 检查变量名
l 变量初始化
n 不恰当的初始化是很大一部分bug的源头
n 包括忘记赋值,赋值过期,对象部分赋值,未分配内存等
l 变量初始化建议
n – 变量声明时紧接着初始化
n – 靠近变量第一次使用时初始化
n – 依据情况使用final(JAVA)或const(C/C++)来防止变量值改变
n – 对计数器和累加器变量要特别小心
n – 在类的构造函数里初始化数据成员
n – 使用变量前确认是否需要重新初始化
n – 检查输入参数的合法性
n – 编译器会发现未使用的变量
n –– 初始化内存
l 变量作用域,scope
n 不同语言处理变量作用域的方式有所不同
n Namespace,package,class,method
n 变量使用集中化,即跨度尽可能小
u • 跨度:两个使用同一变量的语句之间跨越的语句数量。
u • 变量的引用点之间就是可能“不当”修改变量的地方(攻击窗口)
n 变量使用的“存活时间”越短越好
u 存活时间:一个变量开始于使用它的第一条语句,结束于使用它的最后一条 语句,中间所包括的语句数量。
u 可读性提升
u
u 短跨度,短存活时间更好
u
u 选取平均存活时间更少的方案
u ,
u 减小作用域的一般原则
l 循环开始前初始化循环中的变量,而不是前方很远
l – 变量即将使用时赋值
l – 相关语句放到一起
n
l 相关语句组成函数/方法
l 严格作用域和可见性:首选将变量局限于某个特定的循环,然后 是局限于某个函数/方法,其次成为类的private变量,protected变 量,再其次对包(package)可见,最后不得已在作为全局变量 • 不恰当的初始化是
l 变量最好偷偷摸摸的
n 变量持续性
u 很多时候变量的生命周期比开发者认为的要短,由此而来问题就是各种误用。
l 断言、错误处理等防御性编程技术
l 使用变量前养成检查其持续性的习惯(还要检查初始化)
l 养成好习惯:使用所有变量前声明和初始化,同时留心使用位置 和初始化位置较远的变量
n 变量绑定时间
u 绑定是指变量和值的绑定,也就是赋值
u 绑定时间越晚对于程序的灵活性越有利(最后实例化时赋值)
u 编码时,编译时,加载时,实例化时,即时
u
n 数据类型与控制结构
u 单一用途变量,不要追求奇技淫巧。
u
n 变量名
u 变量名命名的基本原则
l 完整准确的表述该变量所代表的事物
n
l 可搜索的名字
n • 在长代码和大型工程文件中搜索查找
n • 易记忆,无歧义,易理解
n
l 问题导向,“what”,not“how”
l 适宜的长度,8-20个字符
l 变量名长度和作用域的关系:短的变量名意味着作用域较小,长 的变量名适用于较少用到的变量和全局变量
n 名称越短越通用越少限定,其作用域必然应该越小
l 全局命名空间是一个很好的策略,
n namespace, C++
n java里是包 package
u 变量名中的计算值限定词
l Total,Sum, Average,Max, Min,Record等放到变量名的最 后
l 名词(核心词)在前,形容词(修饰词)在后
n 重要的内容先看到
n 建议非强制,一致性最重要(整个课程基本都是这样)
n 例外,Num在变量名前部代表一个总数,numCustomers标识客 户总数;Num在变量名后部是指下标,customerNum标识当前客 户序号。
l customerTotal, customerIndex
u 变量名中的常用对仗词
l – begin/end
l – first/last
l – locked/unlocked
l – min/max
l – next/previous
l – old/new
l – opened/closed
l – visible/invisible
l – source/target
l – source/destination
l – up/down
l – ……
u 特定数据类型变量的命名
l – 循环下标
n • i, j, k常用
n • 如果这个下标变量还需要在循环外使用,需要一个更有意义的名字
n • 很长的循环体,简单的i会被忘记,一个有意义的名字也是一个好的选择
n • 嵌套循环的下标变量使用更有描述性的名字
l – 状态变量
n • 不要用“flag”做名字
n • 更有意义的名字,而不是一些数字
n
n
l – 临时变量
n 需要特别警惕temp出现
n 个人建议,每次使用都要重新初始化
n 有些临时变量的名称是可以更有意义的
l – 布尔变量
n 典型布尔变量名:done,error,found,success,ok,etc.
n 注意status,state等不隐含“真\假”含义的名字(就是说最好不要用)
n 该类型变量名应该有明显的真假属性
n “肯定”的变量名,如done,found,不建议notDone,notFound等
l – 枚举变量
n 组前缀
l – 具名变量,
l 常量命名(不变的一般用大写)
n 表示具体含义,而不是数值
n 常全大写
u 命名规则的重要性,naming convention
l – 集中精力
l – 合作交流
l – 减少误用
l 有规则好于没有规则,哪怕这个规则不那么完美
n 标准命名规则(跨语言)
u 区分变量名(小写字母开头)和函数名(大写字母开头)
u 区分类和对象:
l 通过首字母大小写区分类型和变量
n 适用于大小写敏感的语言如 Java,C++,但不适合 多语言混合环境
l 通过全部大写区分类型和变量
n 全大写在Java,C++中 习惯标识常量
l 通过给类型加上“t_”前缀区分类型和变量
n Java和C++中用得少
l 通过给变量+“a”前缀来区分
n 常用的一种手段
l 对变量采用更明确的名字(长一些就会)来区分
n 常用且推荐
u 区分变量名(小写字母开头)和函数名(大写字母开头)
u 区分类和对象:大写字母开头,全部大写,前缀t和a,等
u 标识全局变量
u 标识成员变量
u
u 匈牙利命名法
l 类型前缀的,弱化中
l 语义前缀,非常有用
l
n 可读的短变量名和缩写技巧
u 标准缩写
u 去除虚词
u 去除非前置元音
l ?
u 使用单词第一个或前几个字母,并统一截断
u 取出无用后缀
u 不要超过三个单词
u 要么删除不止一个字母,要么单词拼写完整
u 缩写要一致
u 避免使用易错的组合
u 缩写对照表
u 语音缩写不大推荐
n 不好的名字
u 令人误解的名字或缩写(FALSE,CCTV等)
u 相似含义的名字(input和inputvalue,fileNumber和fileIndex等)
u 发音相同的名字(同音异意词)
u 名字中不要使用数字(a1,b2,c3等)
u 不要拼错或易于拼错(不要高估自己)
u 不要仅靠大小写区分变量名(虽然Java和C++大小写敏感,但此 风不可长)
u 不要使用多种语言(程序员不是语言学家!)
u 不要使用标准类型、变量和函数名(if,then等)
u 与变量含义无关的名字(obviously!)
u 易混淆的字符(1和l,0和o,S和5等)
l 变量使用的一般原则
n 代码中避免直接写数字,可使用常量
u • 修改更可靠
u • 修改更容易
u • 代码可读性
u • 0和1可按需要直接使用
n 避免除零错误
n 显示类型转换(重要哦)
n 避免混合类型比较
u (看着就不得劲)
n 编译器会发现很多数值类型的问题,不要忽视(警告不要不管)
n 整数变量使用
u – 检查整数除法
u – 检查整数溢出
u – 检查中间结果溢出
u
n 浮点数变量使用
u 避免数量级相差过大的数之间的加减运算
l
l 可以先排序后计算,让数量级靠近的数先完成加减计算
u 避免等量判断
l 浮点数计算不同于整数计算,每一步计算都存在误差,所以不同计算路径的 结果可能不同
l 解决方法是自己确定可接受的精确度范围,判断是否足够接近而视作相等。
l
l 有意思
l 处理舍入误差问题
l 检查语言和函数库对特定数据类型的支持
l 。
n 字符和字符串变量使用
u 用常量而不是直接在代码中出现字符或字符串(神秘字符,串)
u 避免off-by-one错误(字符串的下标要注意,不同环境不一样,0 or 1)
u 了解Unicode(Java默认支持,C/C++需要支持)
u 国际化/本地化策略(字符串中的语言)
u C语言下的字符串问题(C++ STL的string类已解决)
u ,
n 布尔变量使用
u 布尔变量一般不会用错,但是用好它非常重要
u 布尔表达式,例如 if 判断
u 简化复杂的判断
l 中间结果和有意义的布尔变量名(搭配实现简化判断)
n 枚举变量使用
u (枚举变量属于常量,enum 枚举名{枚举成员1,枚举成员2,...};)
u ,
u – 提升可读性,状态集
u – 提升可靠性,部分语言有效
u – 简化修改
u – 替代布尔变量
n 常量使用
u 在数据声明中使用具名常量
u 避免直接使用数字
u 统一使用
n 数组使用
u 下标没出边界
u 优先考虑容器而不是数组(栈,队列,链表等)
u 检查数组边界点
u 多维数组要留心下标的使用(多个下标的混用)
n 自定义类型名
u C++
u 代码清晰,可读性更高,变量与数值分离
n 特殊数据类型
u 结构数据
l 明确数据关系(整合,抽象)
l 简化对数据块的操作
l 简化参数列表(当你的函数参数大于7个)
l 减少维护(基本不要改)
u 指针
l 危险而又美丽
l 包括两个部分:内存位置和所指向的内容
l 尽可能在小的不重要的地方使用,严格控制作用域,反复检查, 避免表达式过于复杂,注意删除顺序。(有风险)
l C++指针:区分指针和引用,参数传递要小心,内存释放
n 指针引用区别
u 指针是一个变量,存储的是一个地址,指向内存的一个存储单元;
u 引用是原变量的一个别名,跟原来的变量实质上是同一个东西
u
l C指针:显式指针,检查大小和指向类型
u 全局数据
l 高风险数据类型(少用为妙)
l
l 常见的问题
n 无意间修改全局数据
n 全局变量的别名要仔细,不同语言处理方式不一样
n 全局数据对多线程有较大的影响
n 全局数据不利于代码重用
n 全局数据破坏模块化和封装
l – 可以使用全局数据的场景
n • 保存全局数值
n • 模拟常量
n • 模拟枚举
n • 简化常用数据的使用
n • 消除流浪数据
u 让数据存储在全局变量中,而不是在局部作用域中流浪。
u 全局数据和访问器函数,access routines
n 访问器函数是用于获取或设置数据的函数。它们提供了对数据的受控访问,允许在访问数据时执行额外的逻辑,如验证或转换。
l 替代关系
l 把数据隐藏在类里面,用static等其他方法确保单例
代码改善
l 重构
n 软件重构,refactoring
n 重构的原因
u 、? 云里雾里有点
u – 代码重复
u – 过长的函数
u – 循环过长或嵌套过深
u – 内聚性差的类
u – 类接口抽象不一致
u – 参数列表太长
u – 类内部具有多个完全独立的功能
u – 一个变化导致多个类的相同修改
u – 对继承体系的同样修改
l ?
u – case语句需要做相同的修改(直接继承)
u – 反复对相同一组数据操作
u 成员函数使用其他类比使用自己类还要多
u 不当使用基本数据类型
u 无用的类
u 传递流浪数据的函数
u 中间人对象无所事事
u 某个类和其他类过于亲密
u 函数命名不当
u 类的数据成员为public
u 派生类只使用了基类的很少一部分成员函数
u 注释用于解释难懂的代码
u 使用全局变量
u 函数调用前后有设置代码和收尾代码
u 部分代码为将来准备
l 重构类型
n – 数据级重构
n 语句级重构
n 函数级重构
n – 类实现重构
n 类接口重构
n 系统级重构
l 安全的重构
n 保留修改痕迹
n 小步前进
n 检查点和中断现场增加测试用例
n 加强代码检查
n 评估重构的风险和收益
n 重构不是重写或先写后改
n
l 软件重构,software refactoring
n 修改代码,保持功能,提升性能,可读性,安全与隐私等等
n “在不改变软件可观察行为的前提下改善其内部结构”
n 重构的初衷不是维护和修改系统,而是在于代码改善
n 重构不是“重新构建”软件系统,Re-build
n “焦油坑”的教训
u ?
n 谨慎与可靠
n 重构不是万能
l 重构的基本原则
n 何为重构
u 区分添加新功能和重构
u ?
l 重构可以在添加新功能之前进行,也可以在添加新功能后进行。在添加新功能之前进行重构可以使代码更清晰、更易于扩展。而在添加新功能后进行重构,则是修正和改进已有代码,以提高整体的代码质量和可维护性。
n 为何重构
u – 改进软件设计
u – 易于理解
u – Debug
n 何时重构
u – 添加功能时重构
u – 修复错误时重构
u – 复审代码时重构
n 何时不该重构
u 现有代码不能正常运行
l 重构时机
n • 重复代码
n • 太长的函数
n • 太大的类
n • 太长的参数
n • 发散式变化
n • 霰弹式修改
n • 跨类关系过于紧密
n • 数据泥团
u 关于“数据泥团”,它通常指的是在软件开发过程中,由于数据处理不当导致的问题。例如,数据不一致、数据冗余、数据难以维护等。数据泥团会影响系统的稳定性和可维护性,因此在重构过程中,需要关注数据泥团的问题,并进行相应的优化。
n • 基本类型偏执
n • Switch语句
n • 平行继承
n • 累赘类
n • 不确定的前瞻性
n • 临时字段/变量……
n • 过度耦合的消息链
n • 过度的中间人
n • 类间过于紧密
n • 功能类似的类
n • 不完整的类库
n • 纯数据类
n
n • 有选择的继承
n • 过多的注释
l 重构目标
n 可读性,赏心悦目
u 一个小例子
l 影片出租店程序,计算每一位顾客的消费金额。操作者告诉程序 :顾客租了哪些影片、时间多长,程序根据时间和影片类型计算 出费用。影片分三类:普通、儿童和新片。程序可为常客计算积 分,积分根据租片是否为新片而不同
u
u
u 看不清懒得看了
n 程序可工作,但是,嗯,一言难尽~
n 不符合OO设计
n 可扩展性差
n 可读性差
n ,
n 重构第二步
u 改名字
l 给变量前面+a
l 局部变量直接改名叫结果
l
l
n 重构第三步
u 换位置
u 引用处也要相应修改
u 把可能性大的放前面
u
u
n 重构第四步
u 临时变量
u
u
u ?看不懂是个啥意思
n 重构第五步
u 进一步缩短函数
u
u
n 重构第六步
u 再次去除临时变量
u
u
u ? 依旧看不懂
n
n 重构第七步
u 函数换位置
u
u
u
u ????
u
n 重构第八步
u switch与多态
u
u
u
u
u
u
u
u
u Price分成price和其重构部分(普通片,新片,儿童片)
u
u ???
u
u
u 似懂非懂 可以看看书()
l 测试
n 测试是重构的前提条件!
n 重构离不开测试!
n 每一步修改都要进行测试!
n JUnit和Java
n ,
n 重构函数
u 提取函数
l 本方法的难点在于临时变量的存在,so,可以用以查询取代临时 变量的方法减少临时变量。如果还是很难,把该函数转换成函数 对象
l 临时变量是一种只在调用期间有效,且具有常性的变量。
l
u 合并函数
u 内联临时变量
u 以查询取代临时变量
l 查询函数
l
l 变成函数查询方式
u 解释性变量
u 分解临时变量
u 解除参数赋值
u 以函数对象取代函数
l 复杂的大型函数可以放到一个对象中,局部变量就成为字段,该 大型函数就可分解为多个小型函数
l
l
l
l 好家伙
l ??????
u 替换算法
u • 函数改名
l
u • 添加参数
u • 移除参数
u • 查询函数和修改函数分离
l
u • 加入参数来合并函数
l
l
u • 取消参数来分解函数
l
l
u • 合并参数作为对象
l – 谨慎使用
l – 可能破坏封装
l
l
u • 以函数取代参数
u • 移除设值函数
l
u • 隐藏函数
l
u • 以工厂函数取代构造函数
l
l 工厂函数将对象的创建与对象的业务逻辑分离,使得代码更加模块化,便于维护和扩展。
l 构造函数在全局中定义相同的局部变量容易造成全局污染,而工厂函数可以避免这个问题,因为它是在函数内部进行对象创建的。
u • 向下封装
u • 以异常取代错误代码
u • 以测试取代异常
u
u ,
l 重构对象
n • 搬移函数
u 关键的重构手段,谨慎尝试,允许试错
u
u
u – 检查继承结构
u – 注意函数引用的变化
u
n • 搬移字段
n • 提炼新类
u 过于复杂的类不是好的OO设计
u
u
n • 内联类
n • 隐藏“委托关系”
u 引入中间类来提升类间的封装水平
u
u
n • 移除中间人
n • 加入外部函数
u 一个类需要再加一些功能
u
u ??
u
u 如果已经加过了很多功能呢? 再分呗唉(引用外部的)
n • 加入本地扩展(把外部的合进子类之类的里面,变成内部的)
u – 子类化和包装类化
u
u
n
n 字段上移
u
u
n • 函数上移
n • 构造函数主体上移
u
u
n • 函数下移
u 可能和“提炼子类”结合使用
u
u
n • 字段下移
n • 提炼子类
u 和“提炼类”类似,反映的是“继承”和“委托”的差别
u
u
u
u
u 委派:一个对象请求另一个对象的功能,捕获一个操作并将其发送到另一个对象。 继承:利用extends来扩展一个基类。
u (对象---类)
n • 提炼超类
u 和“提炼类”类似,反映的也还是“继承”和“委托”的差别
u
u
n • 提炼接口
n • 折叠继承体系
n • 构建模板函数
u 与“函数上移”类似
u
u
n • 以委托取代继承
u
n • 以继承取代委托
u
n
l 重构数据
n • 自封装字段
u
u
n • 对象取代字段
u 看似简单的数据项很多时候不简单,会需要很多的数据和处理 • 值对象改成引用对象
u
u
u
n • 引用对象改成值对象
u 值对象意味着每个order都有一个customer客户,就算多份订单属于一个客户也是如此
u – 用工厂函数取代构造函数
u
u
u
n • 对象取代数组
n • 复制被监视数据
n 以类取代类型码
u 1. 基本数据类型:如 int、float、double、boolean、char等。
u 2. 引用数据类型:如类(class)、接口(interface)、数组(array)等。
u
n •以子类取代类型码
u
n 单向关联改成双向关联
u – 被引用类中添加一个专门的函数
u – 反向指针
u
u
l
u
u
n • 双向关联改成单向关联
n • 以字面常量取代数字直接使用
n • 封装字段
n • 封装集合
u
u
u
n ,
l 重构表达式
n 分解表达式
u
n 合并表达式
u
n 合并重复的片段条件
u
n 移除控制标记
u
n 以保卫语句取代嵌套条件表达式
u 条件反转
u
l 大型重构
n 梳理并分割继承体系
u
n 过程化代码转化为面向对象代码
u 数据记录变成对象,大块分成小块,再将方法移入相关对象
u ,
n 领域与显示分离
u MVC
n 提炼继承体系
u 针对某个类建立继承体系,一个子类标识一个特殊情况
u ,
l 总结和反思重构
n 重复代码
u – 提取函数
u – 提取函数+提取超类,提取函数+构建模板函数,……
u – 提取类
u – …
n 过长函数
u – 提取函数
u – 提取函数+查询函数
u – 引入参数对象,保持对象完整
u – 以函数对象取代函数
u – 分解条件表达式
u – …
n 过大的类
u – 提取类
u – 提取子类
u – 提取接口
u – 函数+提取超类
u – 复制被监视的数据
u – …
n 过长的参数列
u – 以函数替代参数
u – 保持对象完整(传递整个对象)
u – 引入参数对象
u – …
n 发散式变化,霰弹式修改
u – 提取类
u – 移动函数,移动字段
u – “一个类受多种变化的影响”vs“一种变化引发多个类相应修改
u ”
u – ……
系统考虑
l 团队规模
n
l 错误域代码规模的关系
n
l 生产率与代码规模
n
l 代码规模对开发过程的影响
n
l 管理构建
n 针对软件构造的管理
u – Good coding
u – 配置管理
u – 进度管理
u – 度量
u – 管理的对象是人
u – 管理管理者
u
u .
u Good Codin
l 制定标准和规范
l – 互为备份
l – 代码复查
l – 代码确认
l – 样本规范
l – 鼓励好的代码
u 配置管理
l – 系统化定义项目工件和处理变化,以使项目保持完整健康的状态
l – 需求变更,设计变更,代码变更
l – 约束变更,规范管理,全面评估,集约处置
l – 版本控制
l – 备份
u 进度管理
l 人月神话,the mythical man-month
l
u
u 进度管理
l 如果你落后了,你应该
l – 扩充团队
l – 缩减规模
l – 其他调整
u 度量
l
u 管理的对象是人
l
u 个体差异和团队差异
l – 敏感问题
n
l 物理环境
l
n 集成
u 集成:将一些独立的软件组件组合成一个完整的系统
u 阶段式集成和增量集成
u
u
u
u
n 布局与风格
u
u 细微之处见美学
l 好看的代码
l – 换行,空白,括号,etc.
l – 人和计算机对程序的理解
l
u 何谓好的布局
l – 准确的表达代码逻辑结构
l – 一致的表达代码逻辑结构
l – 改善可读性
l – 经得起修改
u 布局风格
l
l 控制布局若干注意事项
n – 不要用未缩进的begin-end对
n – 不要两次缩进begin-end对
n – 段落之间用空行
n – 代码布局风格一致
n – 对复杂的表达式,可以分多行表示
n – 不用goto
n – case语句不要有行尾布局的例外
l 单条语句的布局
n – 语句的长度,<80字符
n – 用空格使语句更清晰
n – 如何“断句”?格式化后续行
u • 使后续行明显
u • 紧密联系的内容在一起
u • 函数调用的后续行按标准量缩进
u • 让后续行的结尾易于发现
u • 控制语句的后续行标准缩进
u • 不要把赋值语句按等号对齐
u • 赋值语句的后续行标准缩进
n 每行一条语句
n 数据申明
u • 每行一个数据
u • 变量声明靠近初次使用地方
u • 合理组织声明顺序
u • C++中,声明变量指针时把星号靠近变量名
u ,
n 注释的布局
u – 注释缩进与代码一致
u – 每行注释用至少一个空行分开
n 函数的布局
u – 用空行分隔函数各部分
u – 函数参数按标准缩进
n 类的布局
u – 类接口布局
u 类实现布局
u 文件和程序布局
l • 一个文件一个类
l • 文件的命名与类名有关
l • 清晰分隔各函数
l • 字母顺序排列函数
l • C++源文件的典型顺序
l 自说明代码
n 自说明代码或自解释
n 代码注释
n 注释类型:
u – 重复代码
u – 解释代码
u – 代码标记
u – 概述代码
u – 代码意图说明
u – 代码无法表达的信息
n 高效注释
u – 采用不打断或抑制修改的注释风格
u – 用伪代码编程发减少注释时间
u – 把注释集成到你的开发风格中
u – 性能不是逃避注释的接口
u – 最佳注释量,1:10
n 注释技术
u 注释单行
l • 不要添加无关注释
l • 行尾注释课用于数据声明,其它场合不要轻易使用,尤其不要用于维护记录
u 注释代码段
l • 表达代码段的意图
l • 代码应自说明
l • 注重“why”而不是“how”
l • 说明非常规做法
l • 为后面的内容做铺垫
l • 主次注释区分开
l • 不用缩略语
l • 错误或语言环境独特点加注释
l • 给出违背良好编程风格的理由
l • 不要注释投机取巧的代码,重写之
u 注释数据声明
l • 注释单位
l • 注释数据范围
l • 注释编码含义
l • 注释输入数据限制
l • 注释“位标志”
l • 注释修改过的变量名
l • 注释全局数据
u 注释控制结构
l 注释每个if,case,循环或代码段前面
l 注释每个控制结构后面,解释其后果,但这也是代码过于复杂的征兆
l .
u 注释函数
l – 注释应靠近其说明的代码
l – 在函数上部用一两句话说明
l – 在声明参数出注释这些参数
l – Javadoc
l – 分清输入输出数据
l – 注释接口假设
l – 注释函数局限性
l – 说明函数的全局效果
l – 记录所有算法的来源
l – 用注释标记程序的各部分
u 注释类、文件和程序
l – 注释类的设计方法
l – 说明局限性和用法假设等
l – 注释类接口
l – 不要在类接口处说明实现细节
l – 说明各文件的意图和内容
l – 将姓名、电子邮件及电话号码放到注释
l – 包含版本控制标记
l – 注释块中包含法律通告
l – 文件名与内容相关
n 个人性格
u • 聪明与谦虚
u • 求知欲
u • 诚实 •
u 交流与合作 •
u 创造力和纪律
u • 偷懒
u
u • 矜持
u • 经验
u • 狂热
u
u • 习惯
n 代码整洁
u 整洁的代码如同一幅画卷,美妙无比
u • 开发者作为画师不仅要绘出命题画卷(开发完成功能),也要让它美起来(不美的画卷何用)
u • 画师需要刻苦地、持之以恒、长年累月地去练习,开发者也是
u • 局座说:任何装备一定要好看,好看的装备战斗力问题都不大
软件工艺
l • 降低复杂度
n – 从类与对象,到函数,到变量,再到语句,诸多层面
l 开发过程注重质量优先,团队合作和谨慎修改
l • 代码以人为先,其次才是机器
n 可读性
l 深入一门语言编程,不浮于表面
l 借助规范集中注意力
l • 基于问题域编程
l • 程序划分不同抽象层次
l • 留意各种征兆或标识
l • 迭代,反复。。。
l • 不要过于固执
测试驱动
l 软件测试? 软件工程? 软件构造?
l 测试驱动开发,TDD,Test Driven Developmen
l 敏捷开发中的一项核心实践和技术,也是一种设计方法论 。TDD的思想是在开发功能代码之前,先编写单元测试用 例代码,测试代码确定需要编写什么产品代码。
l TDD = TFD + Refactoring
l • (TFD -- Test First Development)
n TDD三项法则
u – 在编写好单元测试前,不要编写产品代码
u – 只要一个单元测试未通过,就不要再写测试代码;编译未通过也算
u – 产品代码恰好能让单元测试通过即可,无需多写。
n 目标:30秒运行一次代码,反复迭代
l TDD的优势
n – 确定性,对代码的把握
n – 缺陷注入率:bug释放
n – 勇气:确定性带来的自信
n – 文档:单元测试即文档
n – 设计:辅助支持
l 缺点
n – 嗯,代码比较多?
l TDD的一些注意事项
n – 脏测试等于没测试
n – 测试代码不是二等代码
n – Java中每个测试一个断言
n – Junit
l TDD的一些争议
n TDD is dead. Long live testing。
n – 代码那么多要写,功能那么多要实现,那马多的测试工作实际吗?
l
l JUnit使用示例
n – 测试套件,@Suite,@RunWith
n – 忽略测试,@Ignore
n – 时间测试,@Test(timeout=xxx)
n – 异常测试,@Test(expected=xxx)
n – 参数化测试
n – …
《编程珠玑》
l 实用编程技巧
l 代码的效率
l 计算理论和模型 -> 实际用例(珠玑)
l ,
l 看不动了有机会直接看书吧有这么本书就是说
标签:语句,重构,变量,代码,构造,笔记,使用,软件,函数 From: https://www.cnblogs.com/xiaomamm/p/17889179.html