1. 前言
发现和指出问题为了:更好的解决问题和避免问题的再次发生
项目在演进,代码不停地在堆砌。如果代码的质量一直不被重视,代码总是会往越来越混乱的方向演进。当混乱到一定程度之后,量变引起质变,项目的维护成本已经高过重新开发一套新代码的成本,想要再去重构,已经没有人能做到了。
在不改变软件可观察行为的前提下,逐步重构,提高其可理解性,降低其修改成本。
2. 架构设计实现缺陷梳理
2.1 复杂函数
缺陷特征:指的是代码行多,分支嵌套深,变量多,参数多,复杂度高等特征的函数。
缺陷影响:函数不易理解和维护,代码重复、冗余。
解决方法:新开发代码时,函数都是越写越复杂的,应该要有意识地、积极地去分解提炼成小函数或独立功能的函数,甚至当感觉需要以注释来说明点什么的时候,这时其实就应该独立成一个函数。函数建议值:代码行24,if语嵌套深度6,圈复杂度10,功能应该单一。
代码示例:
2.2 数据泥团
缺陷特征:函数的参数多且参数列表相似,反复调用相同的参数列表。
缺陷影响:大量重复,影响编译的效率;参数多,很难理解和调用。
解决方法:参数列表应该封装成结构。建议值:函数参数平均为2,避免5个以上。
代码示例:
int Query::Compile(CompiledQuery *compiled_query, SELECT_LEX *selects_list, SELECT_LEX *last_distinct, TabID *res_tab,
bool ignore_limit, Item *left_expr_for_subselect, common::Operator *oper_for_subselect,
bool ignore_minmax, bool for_subq_in_where)
2.3 过度耦合
缺陷特征:一个函数调用大量其它模块的函数,却调用很少本模块的函数。
缺陷影响:一个函数与多个函数(这些函数属于少数一两个类)联系过于紧密;一个类提供了很多函数给外部某个函数调用;耦合度高,类不够抽象。
解决方法:识别内、外部模块函数,外部模块要足够抽象调用。
代码示例:登录 石原子科技 · 石原子科技
2.4 循环依赖
缺陷特征:多个子系统处于一个环状互相依赖关系里面;函数的调用关系混乱、循环;文件直接或间接交叉引用。
缺陷影响:不易理解和维护,编译慢,关系混乱,重用困难。
解决方法:多文件或系统间要划分清楚结构、层次关系,应做到无环依赖。
伪码示例:循环包含头文件,file A包含file B,而file B又包含了file A。
#0 remove_eq_conds (thd=0x7f3874000e10, cond=0x7f3874006ee0, retcond=0x7f387400f6c0, cond_value=0x7f3874005c88, part=1 '\001')
at /data/stonedb/sql/sql_optimizer.cc:10354
#1 0x0000000002569eb8 in optimize_cond (thd=0x7f3874000e10, cond=0x7f387400f6c0, cond_equal=0x7f387400f6e0,
join_list=0x7f3874005d40, cond_value=0x7f3874005c88, part=1 '\001') at /data/stonedb/sql/sql_optimizer.cc:10114
#2 0x000000000255191f in JOIN::optimize (this=0x7f387400f3c0, part=1 '\001') at /data/stonedb/sql/sql_optimizer.cc:263
#3 0x0000000002e51ded in stonedb::core::optimize_select (thd=0x7f3874000e10, select_options=2147748608, result=0x7f3874007ad8,
select_lex=0x7f3874005ba0, optimize_after_sdb=@0x7f38ea3aadec: 1, free_join=@0x7f38ea3aadf0: 1)
at /data/stonedb/storage/stonedb/core/engine_execute.cpp:352
#4 0x0000000002e516a0 in stonedb::core::Engine::HandleSelect (this=0x7fe4d90, thd=0x7f3874000e10, lex=0x7f3874003138,
result=@0x7f38ea3aadf8: 0x7f3874007ad8, setup_tables_done_option=0, res=@0x7f38ea3aadf4: 0,
optimize_after_sdb=@0x7f38ea3aadec: 1, sdb_free_join=@0x7f38ea3aadf0: 1, with_insert=0)
at /data/stonedb/storage/stonedb/core/engine_execute.cpp:225
#5 0x0000000002f39e86 in stonedb::dbhandler::SDB_HandleSelect (thd=0x7f3874000e10, lex=0x7f3874003138,
result=@0x7f38ea3aadf8: 0x7f3874007ad8, setup_tables_done_option=0, res=@0x7f38ea3aadf4: 0,
optimize_after_sdb=@0x7f38ea3aadec: 1, sdb_free_join=@0x7f38ea3aadf0: 1, with_insert=0)
at /data/stonedb/storage/stonedb/handler/ha_rcengine.cpp:82
#6 0x000000000257e6c8 in execute_sqlcom_select (thd=0x7f3874000e10, all_tables=0x7f3874007158)
at /data/stonedb/sql/sql_parse.cc:5182
#7 0x0000000002577a50 in mysql_execute_command (thd=0x7f3874000e10, first_level=true) at /data/stonedb/sql/sql_parse.cc:2831
#8 0x000000000257f6a1 in mysql_parse (thd=0x7f3874000e10, parser_state=0x7f38ea3abf90) at /data/stonedb/sql/sql_parse.cc:5621
#9 0x000000000257492f in dispatch_command (thd=0x7f3874000e10, com_data=0x7f38ea3ac730, command=COM_QUERY)
at /data/stonedb/sql/sql_parse.cc:1495
#10 0x000000000257385f in do_command (thd=0x7f3874000e10) at /data/stonedb/sql/sql_parse.cc:1034
#11 0x00000000026a62d3 in handle_connection (arg=0xab65240)
at /data/stonedb/sql/conn_handler/connection_handler_per_thread.cc:313
#12 0x0000000002d88c7e in pfs_spawn_thread (arg=0x8129da0) at /data/stonedb/storage/perfschema/pfs.cc:2197
#13 0x00007f39361aa609 in start_thread (arg=<optimized out>) at pthread_create.c:477
#14 0x00007f3935ad1133 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
2.5 复杂类
缺陷特征:规模非常庞大、复杂性高的类,常常包含多个复杂函数,有多重功能。
缺陷影响:圈复杂度高,内聚性差,耦合度高,不易看懂和维护。
解决方法:解决复杂函数,结构要清晰,类功能应该单一。建议值:类行数应在2000以内。
代码示例:
2.6 全能类
缺陷特征:一个类集中了多个不相关类的功能;一个类操作其它模块数据太多;大而复杂。
缺陷影响:破坏了类的封装性,耦合度高,内聚性差,不易维护。
解决方法:多个功能不相关的类应该分别封装成不同的类,适当搬移函数,解决复杂函数问题。
代码示例:
2.7 重复代码(Repeat code)
缺陷特征:不同模块或文件间有类似或重复功能的类;不同类间有类似或重复功能的函数;同一父类的子类间存在相似或重复功能的代码。
缺陷影响:代码膨胀混乱,不易维护,本来维护一处代码由于重复代码要维护多处。
解决方法:提炼重复代码。如工具函数封装成工具类,通用功能封装成公共库。
代码示例:
2.8 Shotgun Surgery(霰弹式修改)
如果每遇到某种变化,你都必须在许多不同的类作出小修改,你所面临的坏味道就是Shotgun Surgery,你如果需要修改代码有很多处,你不但很难找到它们,也很容易忘记某个重要的修改。