写在前面
因为是机械专业的关系
开始真正意义上进行大规模开发大概接近半年
自己代码基础比较差,前段时间沉浸于C++的特性等一类的面试八股文中
领导点醒,有用(有在使用)才是有用的东西(记得住,能理解)
目前存在的阶段应该是:如何写一段优美的代码,也是目前在看的书<重构>
之后再是对项目立项的整个流程理解,如何与产品,测试对接,明白大项目是如何运行的
再者就是对公司软件架构的理解,深挖,大软件如何去运行
完成<代码大全>的阅读和理解
<重构>这本书真是越看越有味道, 目前开发经验尚浅,待明年会进行二刷。
小tips
1.如果想替换掉一个正在使用的库,可以先引入一层新的抽象,使其兼容新旧两个库的接口。
2.编写测试代码还能帮我把注意力集中于接口而非实现(这永远是一件好事),撰写测试代码的最好时机是在开始动手编码之前
3.每当你遇见一个bug,先写一个测试来清楚地复现它。仅当测试通过时,才视为bug修完。
4.数据的作用域越大,对于数据的封装就越重要,数据最好限制在尽量小的作用域。
5.利于代码复用,需将业务和(界面/实现)逻辑分开
6.需要多增加一个条件判断是一件很危险的事情,代表代码紧耦合,可维护性差,尝试使用管道、多态来解决。
7.“标记参数”是这样的一种参数:调用者用它来指示被调函数应该执行哪一部分逻辑,拆成多个函数,少用标记参数。
8.把全局数据用一个函数包装起来,将这个函数(及其封装的数据)搬移到一个类或模块中,只允许模块内的代码使用它,从而尽量控制其作用域
https://www.kancloud.cn/erull/reconsitution/1099667
9.在程序刚开发出来还在,随着软件系统的能力不断演进时,上下文边界通常不是那么清晰
·如果发生变化的两个方向自然地形成了先后次序(比如说,先从数据库取出数据,再对其进行金融逻辑处理),就可以用拆分阶段(154)将两者分开,两者之间通过一个清晰的数据结构进行沟通。
·如果两个方向之间有更多的来回调用,就应该先创建适当的模块,然后用搬移函数(198)把处理逻辑分开。
10.函数签名(Signature):定义了函数的输入输出。 签名可包含以下内容: 参数及参数的类型 一个返回值及类型 可能会抛出或传回的异常
重构好处
1.架构的整体设计,长远目标
2.更易理解,不然为难别人,为难自己
3.重构能帮忙找bug
4.经济,健硕性,持续性,易改性
重构原则
1.重构前需要有自动化测试集,自动检查对比自己的测试结果。
2.小步修改(随时能停止,不影响功能)越复杂的问题,越需要分解,养成思维惯性(拆解难题)
3.大多数情况下,你认为的性能影响都微乎其微,易懂>效率
4.营地法则:保证你离开时的代码库一定比来时更健康
5.把逻辑从处理更新操作的代码中搬移出来,将没有副作用的代码与执行数据更新操作的代码分开。避免返回值被中途被修改。
6.写不久的代码的重构,熟悉时重构。
重构时机
1.代码不易更改,重构的最佳时机就在添加新功能之前。
2.重复逻辑,提炼重复逻辑为函数,并去复用,复用实现手段:拆阶段/调顺序
3.需求频繁变更
4.逻辑理解费劲,函数难以想出一个好名字的时候
5.函数过长,传参过多(多个函数相同参数可组合成类),局部变量过多
6.想写注释时,注释的代码段需不需要提炼成函数,并起一个没有废话的函数名(起名字的时候想想如何精简名字),同时可以使用减少传入参数个数。过多的注释也需要考虑重构。
7.只有当我需要理解其工作原理时,对其进行重构才有价值。
8.如果重写比重构还容易,就别重构了
9.如果一块代码我很少触碰,它不会经常给我带来麻烦,那么我就倾向于不去重构它
10. 重复代码,需要ctrl+c/v的时候需要停顿一下思考一下
11.全局变量:把全局数据用一个函数包装起来,至少你就能看见修改它的地方
PS: 封装变量(132)来确保所有数据更新操作都通过很少几个函数来进行,使其更容易监控和演进
12. 过长函数,格式函数的意义: 让一个函数尽量有一定的格式,使其易懂
13. 过大的类
14. 当修改一个类/模块时,需要在多处进行调整,可考虑用函数组合成变换、组合成类;内联函数/内联类。当软件层级架构比较明确的时候,尽量在底层做,然后暴露给顶层。
15. 依恋情结:某个函数为了计算某个值,从另一个对象那儿调用了几乎半打的取值函数
16. 数据泥团:两个类中相同的字段,许多函数签名中相同的参数,类调整。
https://www.jianshu.com/p/85c79622d958
17.令人迷惑的暂时字段, 拒绝各种无厘头的magic number,或者不明意义的类成员
18.被拒绝的遗赠:子类应该继承父类的函数和数据,但如果他们不想要或者不需要。
19. 过度耦合的消息链、不完美的库类
20. 中间人:某个类接口有一半的函数都委托给其他类
21.其他问题: 做了不必要的封装,冗赘类,平行继承体系,过度设计,两个类过于亲密,异曲同工的类
重构方法
我觉得这位哥们写的以及非常精炼了,不理解的点直接百度,配合着用就比较清晰了
http://t.zoukankan.com/Gabby-p-8206865.html
下面是读者的一些笔记,上面方法中很多都是能想到的,就没展开叙述,也仅作为一个记录与思考,会不断补充
·提炼函数(反过来就是函数内联化)
·时机:时时刻刻
·步骤:调整代码段–无废话的名字(注重做什么)–变量处理–运行–查找其他–测试
·局部变量处理:以查询取代临时变量(封装返回值接口)/引入参数对象(结构体或者类)
·杀手锏:以命令取代函数(将函数封装成自己的对象)
·其他信号:代码前方有一行注释/ifelse/switch/while(if与else语句块中的内容比较复杂,将其封装成函数)
(把switch中每个分支逻辑创建一个类,用多态来承载各个类型特有的行为:一个分支一个类用简单工厂模式(只要知道用户类型,工厂就实例化出合适的对象)返回一个多态对象)
(一个循环干一件事情,这样做的好处是 当你需要重新修改某一件事情,你就可以直接写个改件是的业务逻辑,而不用去看这个修改会不会影响到第二件事情。同时使用管道方法替代for循环也是一种代码的提升)
http://t.zoukankan.com/hellovenus-p-cpp_polymorphism.html
以管道取代循环
https://blog.csdn.net/fjjaylz/article/details/109962864
(有一个改进函数名字的好办法:先写一句注释描这个函数的用途,再把这句缩句变成函数的名字。)
·提炼变量(反过来就是变量内联化)
·引入解释性临时变量
为一段神奇的字段(运算)起个好名字(临时变量),让逻辑更加顺畅
·以查询取代临时变量
字段通过写个函数去get,而不直接名为临时变量
减少局部变量的传递,常与提炼函数一起用
只适用于那些只被计算一次且之后不再被修改的变量。
如果不是在类中,顶层函数中拥有过多参数,这将冲淡提炼函数所能带来的诸多好处
·封装变量
时机:如果想要搬移一处被广泛使用的数据,最好的办法往往是先以函数形式封装所有对该数据的访问。
做法:新函数返回—逐一修改使用该变量的代码,将其改为调用合适的封装函数,每次替换之后,执行测试。
好处:可以轻松地添加数据被修改时的验证或后续逻辑
即便在类内部,也应该通过访问函数来使用字段——这种做法也称为“自封装”。
·分解临时变量
如果临时变量承担多个责任,它就应该被替换(分解)为多个临时变量,每个临时变量只承担一个责任。
·移除对传入参数的赋值动作
最后再赋值,避免错误返回导致参数赋一半
·搬移函数/字段
其使用另一个对象的次数要多于使用自己所驻对象的次数。
https://blog.csdn.net/pistolove/article/details/42679983
·提纯类/将类内联化
·隐藏委托关系/移除中间人
person.get_department()->get_manager().getName(); //隐藏委托类
person.getManager().getName(); // 客户端调用方式
移除中间人:某个类做了过多简单委托工作;让客户直接调用委托类。
·引入外加函数
无法修改这个类,将实例传入一个函数处理
·引入本地扩展
子类 或 包装类(包含这个类的指针的类)
你需要为服务类提供一些额外函数,但你无法修改这个类。
·以对象取代数据值: 数据同生共灭
·将实值对象改为引用对象/将引用对象改为值对象
·以对象取代数组
·复制被监视数据: 数据同步
·单向、双向关联互改
两个类都需要使用对方特性,但其间只有一条单向连接。 添加一个反向指针,并使修改函数能够同时更新两条连接。
两个类之间有双向关联,但其中一个类如今不再需要另一个类的特性。 去除不必要的关联
· 以符号常量/字面常量取代魔法数
·类的数据安全需要考虑,暴露修改接口
·以类取代类型码
·以字段取代子类(化子类为属性)
·以测试取代异常
面对一个调用者可以预先检查的条件,你抛出了一个异常修改调用者,使它在调用之前先做检查
·分解条件式 if else 对齐而不是嵌套
·合并重复的条件执行片段
在条件表达式的每个分支上有着相同的一段代码。将这段重复代码搬移到条件表达式之外。
·移除控制标记
在一系列布尔表达式中,某个变量带有“控制标记”(control flag)的作用。以break语句或return语句取代控制标记。
·引入NULL对象
当你需要再三检查某个对象是否为NULL。将NULL值替换为NULL对象。
·以委托取代继承
·以测试取代异常