在软件设计开发中,代码的设计都体现在:子系统与子系统、模块与模块、函数与函数之间的关系,设计越糟糕的软件,维护成本越高,质量也往往难以达标和称赞。
好的设计必定是:层次关系简洁、清晰、易维护和扩展的。
不会研究太高深的设计,只总结出一些常见的代码设计缺陷,这些设计缺陷如能很好的解决和避免,相信代码能力(编写、设计、评审、重构)能提高一个档次。
主要介绍下面15个常见代码设计缺陷:
缺陷特征:指的是代码行多,分支嵌套深,变量多,参数多,注释多,复杂度高等特征的函数。
缺陷影响:函数不易理解和维护,代码重复、冗余。
解决方法:新开发代码时,函数都是越写越复杂的,应该要有意识地、积极地去分解提炼成小函数或独立功能的函数,甚至当感觉需要以注释来说明点什么的时候,这时其实就应该独立成一个函数。函数建议值:代码行24,if语嵌套深度6,圈复杂度10,功能应该单一。
缺陷特征:函数的参数多且参数列表相似,反复调用相同的参数列表。
缺陷影响:大量重复,影响编译的效率;参数多,很难理解和调用。
解决方法:参数列表应该封装成结构。建议值:函数参数平均为2,避免5个以上。
伪码示例:GetDate(int year,int month,int day,int time) -> GetDate(struct DateRange)。
缺陷特征:包含某个头文件,但是却没有使用头文件中任何内容。
缺陷影响:编译链接速度慢,耦合度高,头文件错误包含,如包含某个头文件却没有使用里面的内容,某个头文件却依赖某个dll,则会引起不必要的dll依赖和错误。
解决方法:头文件不能乱包含,100%确认每个包含的头文件使用情况,删除不必要包含的头文件。
缺陷特征:一个函数调用大量其它模块的函数,却调用很少本模块的函数。
缺陷影响:一个函数与多个函数(这些函数属于少数一两个类)联系过于紧密;一个类提供了很多函数给外部某个函数调用;耦合度高,类不够抽象。
解决方法:识别内、外部模块函数,外部模块要足够抽象调用。
缺陷特征:多个子系统处于一个环状互相依赖关系里面;函数的调用关系混乱、循环;文件直接或间接交叉引用。
缺陷影响:不易理解和维护,编译慢,关系混乱,重用困难。
解决方法:多文件或系统间要划分清楚结构、层次关系,应做到无环依赖。
伪码示例:循环包含头文件,file A包含file B,而file B又包含了file A。
缺陷特征:函数很少访问自己模块数据,总是访问外部模块数据;访问自己模块少,访问其它模块多;数据和操作不在同一模块;对其它类的数据比较感兴趣。
缺陷影响:耦合度高。
解决方法:同一模块的数据和操作应该放在一起。
缺陷特征:不同模块或文件间有类似或重复功能的类;不同类间有类似或重复功能的函数;同一父类的子类间存在相似或重复功能的代码。
缺陷影响:代码膨胀混乱,不易维护,本来维护一处代码由于重复代码要维护多处。
解决方法:提炼重复代码。如工具函数封装成工具类,通用功能封装成公共库。
缺陷特征:一个子系统或模块依赖于另一个比它更不稳定的子系统或模块,如上层模块依赖于不稳定的底层模块,上层模块肯定会问题不断。
缺陷影响:不独立,不稳定,牵一发而动全身。
解决方法:当有依赖关系时,一定要先保证被依赖子系统或模块的稳定性。至少应保证不稳定的子系统要依赖稳定的子系统。
缺陷特征:设计并实现了很多接口,大部分未使用或只在内部使用;定义了很多全局变量,大部分其它模块未使用。
缺陷影响:冗余,设计过度,暴露可视化。
解决方法:按需设计接口,不需要对外公开的变量和函数应该私有化。
缺陷特征:一个类实现了多个不同的功能,如界面类又处理了业务相关的功能。
缺陷影响:不易理解,耦合度高,公共方法太多。
解决方法:对多个功能进行拆分。
缺陷特征:规模非常庞大、复杂性高的类,常常包含多个复杂函数,有多重功能。
缺陷影响:圈复杂度高,内聚性差,耦合度高,不易看懂和维护。
解决方法:解决复杂函数,结构要清晰,类功能应该单一。建议值:类行数应在2000以内。
缺陷特征:一个类集中了多个不相关类的功能;一个类操作其它模块数据太多;大而复杂。
缺陷影响:破坏了类的封装性,耦合度高,内聚性差,不易维护。
解决方法:多个功能不相关的类应该分别封装成不同的类,适当搬移函数,解决复杂函数问题。
缺陷特征:类的继承关系比较深。
缺陷影响:复杂度高,不易维护。
解决方法:类的继承层次结构不应该超过6。
缺陷特征:提供许多公共属性和函数,供很多其它类来操作,自己却很少操作。
缺陷影响:非面向对象,缺乏封装性,不易维护。
解决方法:封装性。
缺陷特征:派生类几乎没有使用任何继承父类的功能,却增加了全新的功能。
缺陷影响:非继承关系却继承,难理解,不易维护。
解决方法:理清类与类之间的继承关系,不适合继承关系的类应该单独分开。