象相合 发表于 2017-12-17 21:25

《重构-改善既有代码设计》【8-9】章整理学习分享(重构手法:组织数据/条件表达式)

本帖最后由 象相合 于 2017-12-17 21:35 编辑

      大家好,今天继续给大家分享重构手法,以下是文章主要内容:

            1. 更新的重构原则。
            2. 重新组织数据系手法。
            3. 简化条件表达式系手法。
            4. 项目分享。
      
         随着“重构原则”的充实,整体收获并没有那么大了,因此项目分享在数据处理系后停更。相应给出比较好理解的一句话概括。
         这个编辑器有点难受,我复制自己写的发送的时候居然标题全变成1. 了。=

      一、重构原则更新:
               
[*]为即将修改的代码建立一组可靠的测试环境。
[*]临时变量助长冗长而复杂的函数。
[*]最好不要再另一个对象的属性基础上运用switch语句。如果要switch也要在类自己的数据上使用。
[*]事不过三,三则重构。
[*]首先写出可调的程序,然后调整它以求获得足够速度。
[*]一个好的名字可以省去读函数内容的操作。
[*]每当需要注释说明时,可以被写进一个函数里,并以用途命名,关键不在函数长度,而在函数做什么,怎么做。
[*]条件和循环也是提炼信号。
[*]针对外界变化的所有相应修改,应该只发生在单一类中,这个类的所有内容随外界变化。
[*]一个函数使用几个类的功能,它应该被放在最多被此函数使用的数据一起。
[*]对于出现在不同类/方法内的一些绑在一起的数据,应该给它们设置对象。
[*]少用switch,switch代表重复。
[*]两个帽子原则:不要边写代码边重构,带上写代码的帽子,觉得需要重构了,再带上重构的帽子去做,如此反复。
[*]注释多写为什么这样,过多注释不一定是好,尝试重构。
[*]先编写测试代码可以将关注点放在接口而非实现。
[*]每当出现一个BUG,先写一个测试单元测试BUG。不要修改以前的测试单元,因为它可能会在修复BUG后出错。
[*]测试:异常,边界
[*]测试太多带来的效益呈现递减态势。但是我们尽量测试大多数BUG。
[*]小步前进,频繁测试。
[*]即使想要提炼的函数非常简单,只要新函数名能更好地示意代码意图,也应该提炼它。
[*]重构出来的函数优先使用private修饰,日后可以慢慢放开。
[*]重构时可能会有性能问题,但这不是主要问题,如果没有重构,好的优化方案就可能与你失之交臂。性能实在太糟糕,临时变量放回去也很容易。
[*]临时变量如果被赋值多次,尽量使它们只使用一次。
[*]JAVA只采用按值传递参数,因此对参数赋值一般不可取,不要对参数赋值。
[*]public不是java的全部,java值得封装的东西有很多。
[*]先写测试,再写类。
[*]去除控制标记,语句会清晰明了。
[*]“每一个函数只能有一个入口和一个出口”的观念会使人陷入代码的坏味道。
[*]条件反转有时候可以帮助写清晰的代码。
[*]if then else也代表重复,尽量不要写。
[*]使用空对象可以方便的处理很多东西,它一定是常量。非常适合使用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对象
[*]使用空对象可以方便的处理很多东西,它一定是常量。非常适合使用Singleton模式。
[*]用空对象继承原对象,原对象isNull为false空对象为true,以后.isNull()即可知道是否为空。甚至可以添加一个接口Nullable昭示大家这是一个空对象。
[*]空对象是一个特例类。


    8. Introduce Assertion引入断言 292

[*]某一段代码需要对程序状态做出某种假设:以断言明确表现这种假设
[*]使用断言“一定为真”的情况,可以帮助程序不出错,滥用断言会导致逻辑混乱。

      今天分享就到这里,祝大家学习进步!

象相合 发表于 2017-12-17 21:36

编辑器有毒,小BUG调的很难受。

kk1212 发表于 2017-12-17 21:42

感谢楼主分享的重构手法笔记。

sxtylhg 发表于 2017-12-17 22:38

很好的资料,很好的教材!学习了

mixthe 发表于 2017-12-17 23:16

笔记太正式,不像自己总结的,要用自己的话来说

L__ 发表于 2017-12-18 13:06

重构手法笔记分享不错

_vision 发表于 2017-12-22 03:26

学习了,加油!
页: [1]
查看完整版本: 《重构-改善既有代码设计》【8-9】章整理学习分享(重构手法:组织数据/条件表达式)