重构-改善既有代码设计【8-9】章整理学习分享(重构手法:组织数据/条件表达式
笑哥2017-12-18笑哥共享网
大家好,今天继续给大家分享重构手法,以下是文章主要内容:
1. 更新的重构原则。
2. 重新组织数据系手法。
3. 简化条件表达式系手法。
4. 项目分享。
随着“重构原则”的充实,整体收获并没有那么大了,因此项目分享在数据处理系后停更。相应给出比较好理解的一句话概括。
这个编辑器有点难受,我复制自己写的发送的时候居然标题全变成1. 了。=
一、重构原则更新:
为即将修改的代码建立一组可靠的测试环境。
临时变量助长冗长而复杂的函数。
最好不要再另一个对象的属性基础上运用switch语句。如果要switch也要在类自己的数据上使用。
事不过三,三则重构。
首先写出可调的程序,然后调整它以求获得足够速度。
一个好的名字可以省去读函数内容的操作。
每当需要注释说明时,可以被写进一个函数里,并以用途命名,关键不在函数长度,而在函数做什么,怎么做。
条件和循环也是提炼信号。
针对外界变化的所有相应修改,应该只发生在单一类中,这个类的所有内容随外界变化。
一个函数使用几个类的功能,它应该被放在最多被此函数使用的数据一起。
对于出现在不同类/方法内的一些绑在一起的数据,应该给它们设置对象。
少用switch,switch代表重复。
两个帽子原则:不要边写代码边重构,带上写代码的帽子,觉得需要重构了,再带上重构的帽子去做,如此反复。
注释多写为什么这样,过多注释不一定是好,尝试重构。
先编写测试代码可以将关注点放在接口而非实现。
每当出现一个BUG,先写一个测试单元测试BUG。不要修改以前的测试单元,因为它可能会在修复BUG后出错。
测试:异常,边界
测试太多带来的效益呈现递减态势。但是我们尽量测试大多数BUG。
小步前进,频繁测试。
即使想要提炼的函数非常简单,只要新函数名能更好地示意代码意图,也应该提炼它。
重构出来的函数优先使用private修饰,日后可以慢慢放开。
重构时可能会有性能问题,但这不是主要问题,如果没有重构,好的优化方案就可能与你失之交臂。性能实在太糟糕,临时变量放回去也很容易。
临时变量如果被赋值多次,尽量使它们只使用一次。
JAVA只采用按值传递参数,因此对参数赋值一般不可取,不要对参数赋值。
public不是java的全部,java值得封装的东西有很多。
先写测试,再写类。
去除控制标记,语句会清晰明了。
“每一个函数只能有一个入口和一个出口”的观念会使人陷入代码的坏味道。
条件反转有时候可以帮助写清晰的代码。
if then else也代表重复,尽量不要写。
[size=13.3333px]使用空对象可以方便的处理很多东西,它一定是常量。非常适合使用Singleton模式。
二、重新组织数据系手法:
Self Encapsulate Field自我封装字段 196
直接访问一个字段,但字段之间耦合关系逐渐笨拙:为字段生成get/set方法,以函数访问。
2. Replace Data Value with Object以对象取代数据值 200
有一个数据项,需要与其他数据和行为一起使用才有意义:将数据变成对象。
3. Change Value to Reference将值对象改为引用对象 204
一个类衍生许多彼此相等实例,需要将它们替换为同一个对象:将这个值变为引用对象。
使用Replace Constructor with Facotry Method->测试->决定对象负责提供访问新对象的途径->决定引用对象预先创建好还是动态创建->修改工厂函数,令其返回引用对象->测试
4. Change Reference to Value将引用对象改为值对象 208
有一个引用对象,很小且不可变,且不易管理:将它变成一个值对象。
值对象的特性,它是不可变的。
使用Remove Setting Method(300)将它变为不可变->建立equalss()和hashCode()->测试->考虑删除工厂,构造声明函数为public
5. Replace Array with Object以对象取代数组
有一个数组,元素格子代表不同东西:以对象替换数组,对于数组中每个元素,以一个字段表示。
6. Duplicate Observed Data复制“被监视数据”
有一些领域数据置身于GUI控件,领域函数需要访问这些数据:将数据复制到领域对象,建立Observer模式,用以同步领域对象和GUI对象内的重复数据。
7. Change Unidirectional Association to Bidirectional将单向关联改为双向关联
两个类需要使用对方特性,但其间只有一条单向连接:添加反向指针,并使修改函数能同时更新两条连接。
使用不当的话,反向指针容易造成混乱,但只要习惯这种手法,其实并不太复杂。
在被引用类中增加一个字段,保存反向指针->决定由哪个类(引用端还是被引用端)控制关联关系->在被控端建立一个辅助函数,命名应指出它的有限用途->如果既有的修改函数在控制端,让它负责更新反向指针->如果在被控端,就在控制端建立一个控制函数,并让既有的修改函数调用这个新建的控制函数。
8. Change Bidirectional Association to Unidirectional将双向关联改为单向关联 225
两个类之间有双向关联,但如今其中一个类不再需要另一个类的特性。
找出该去除的指针->如果使用了取值函数,先用Self Encapsulate Field将待删除字段自我封装,然后使用Subsititute Algorithm对付取值函数。->如果客户未使用取值函数,直接修改带删除字段的所有被引用点。->如果没有任何函数使用待删除字段,移除所有对字段的更新逻辑。
9. Replace Magic Number with Symbolic Constant以字面常量取代魔法数
有一个字面数值,带有特别含义:创造一个常量,根据意义命名,并将上述字面数值替换为这个常量。
声明一个常量->找出引用点->检查是否可使用新声明变量替换魔法数->魔法数替换完毕,整个程序应运转如常。
10. Encapsulate Field封装字段
类中存在一个public字段:将它声明为private,并提供相应访问函数。
提供get/set。
11. Encapsulate Collection封装集合
有个函数返回集合:让这个函数返回集合的只读副本,并在这个类中提供添加/移除集合元素的函数。
12. Replace Record with Data Class以数据类取代记录
需要面对传统编程环境中记录结构:为该记录创建一个哑数据对象。
新建一个类表示这个记录->对于每项数据,在新建类中建立对应private字段,并提供相应取值/设值函数。
13. Replace Type Code with Class以类取代类型码
类之中一个数值类型码,但并不影响类行为:以一个新的类替换该数值类型码。
14. Replace Type Code with Subclasses以子类取代类型码 248
有一个不可变类型码,它会影响类的行为:以子类取代这个类型码。
15. Replace Type Code with State/Strategy以State/Strategy取代类型码 252
有一个类型码,会影响类的行为,但无法通过继承消除:以状态对象取代类型码。
类型码抽象成类,可进行的操作就会变多。
16. Replace Subclass with Fields以字段取代子类 257
各个子类的唯一差别只在“返回常量数据”函数上:修改这些函数,使它们返回超类中某个新增字段,然后销毁子类。
复杂而冗余的类关系会导致代码的坏味道。
三、简化条件表达式系手法:
1. Decompose Conditional分解条件表达式 263
有一个复杂的条件语句:从if then else 三个段落中分别提炼出独立函数。
逻辑里的东西是非常难读的,与其加一个注释不如直接Extract Method。
2. ConsolIDAte Conditional Expression合并条件表达式 265
有一系列条件测试,都得到相同效果:将这些测试合并为一个条件表达式,并将这个条件表达式提炼成独立函数。
有时候有一堆if的测试也很难读,干脆把它们一起提取了。
一堆if 使用或 ;if嵌套使用 与
3. Consolidate Duplicate Coditional Fragments合并重复的条件片段 268
再条件表达式的每个分支上有相同的一段代码:将这段重复代码搬移到条件表达式外。
去除重复代码。
4. Remove Control Flag移除控制标记 270
在一系列bool表达式中,某个变量带有“控制标记”作用:以break语句或return 语句取代控制标记。
去除控制标记,语句会清晰明了。
有时候会有副作用,使用Separate Query from Modifier将副作用移除。
5. Replace Nested Conditinal with Guard Clauses 以卫语句取代嵌套条件表达式 275
函数中条件逻辑使人难以看清正常执行路径:使用卫语句表现所有特殊情况。
就是不用else,全部用if/return达成一步步过滤的状态。
“每一个函数只能有一个入口和一个出口”的观念会使人陷入代码的坏味道。
条件反转有时候可以帮助写清晰的代码。
6. Replace Conditinal with Polymorphism 以多态取代条件表达式 280
手上有个条件表达式,它根据对象类型不同选择不同行为。:将这个条件表达式的每个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数。
就是使用多态把switch给拆开到类里面去了。
7. Introduce Null Object引入null对象 285
你需要再三检查某对象是否为Null: 将null值替换为null对象
[size=13.3333px]使用空对象可以方便的处理很多东西,它一定是常量。非常适合使用Singleton模式。
[size=13.3333px]用空对象继承原对象,原对象isNull为false空对象为true,以后.isNull()即可知道是否为空。甚至可以添加一个接口Nullable昭示大家这是一个空对象。
空对象是一个特例类。
8. Introduce Assertion引入断言 292
某一段代码需要对程序状态做出某种假设:以断言明确表现这种假设
使用断言“一定为真”的情况,可以帮助程序不出错,滥用断言会导致逻辑混乱。
本站所有工具纯属免费共享,请学会感恩作者,无脑喷子永封IP段+删帐号所有评论 不喜欢请右上角X