实例说明什么是代码的坏味道,如何重构

所谓优雅的代码,或者恶心的代码,很多时候是见仁见智的。也同时是看个人喜好或者习惯的。当经验不足,看的和写的代码还不够多的时候,我们可能会追捧某个大神或者奉某本经典为圭臬。然后跟学校的学弟们说,有空多看看《重构》和《设计模式》吧。

在我看来,优雅的代码并不是说这个代码写的有多神,多么让人惊叹。能够让人清晰的去阅读去理解就是好的代码。代码并不是艺术,更多的是严谨的表达出自己的思路。在这个过程中代码的易读性是第一位的,然后是正确性,然后是运行效率。

让人感到恼火的代码也并一定是写的多么凌乱。它们可能非常的中规中矩,甚至是以大量高精尖的设计模式为基础的。但是相信我,阅读这样的代码绝对不是一件轻松的事情。比如ACE,它封装socket后的代码量甚至比操作系统实现socket还要大,而且大很多。我需要你封装后能够简化概念,让我使用某个功能变得更加容易、更加健壮,让我不用操心底层细节,但是ACE的结果是让你知道了更多的概念,让你需要注意更多的细节,让你的网络功能更加脆弱。这样的封装意义何在?

当然,凡事皆有例外。有很多库是代码几乎没法看,但是功能强大到无以复加。并且它们几乎是不可替代的。比如freetype,比如curl,比如stl......  这有点像一个人长的丑是缺点,但是如果有才华的话,那么缺点就不再是缺点,如果有大才的话,那么缺点反而会成为特点。这些不是我辈可以效仿和学习的,只能膜拜。

最近我一直在为光效编辑和光效渲染发愁(可恨没有使用Unity,原本这些不应该我来操心的)。仔细钻研了Particle Universe的代码。按理说,它的代码风格是沿袭了OGRE的代码风格,曾几何时我还是相当膜拜OGRE的设计思路和框架的。但是现在我非常讨厌绕来绕去的代码,举个例子,相比起一个文件3w行代码,我更加无法忍受的是3w行代码分到300个文件中。

Particle Universe中有这么几个核心的组件,一个是实际起作用的组件如Emitter、Affector等,一个是其对应的解析器如EmitterToken、AffectorToken,第三个是其对应的属性窗口,如EmitterPropertyWindow、AffectorPropertyWindow。

如上图所示,我要添加一个新的属性,要修改三个文件、N个地方,如发射器类、解析脚本、写入脚本、属性窗口初始化、属性窗口赋值、属性修改等等。这就是代码的坏味道。它不利于扩展,不利于维护,不利于阅读。从这里也可以看出写编辑器,语音支持反射是多么重要的一件事情。比如Unity使用C#,QT花了很大力气实现了属性功能。

那么我会如何重构它呢?回答是,我暂时不会去动它。因为重构的首要前提是维持功能不变。这套东西是代码的基础,一动就是伤筋动骨,在没有详细测试和特效做支撑的情况下,学会习惯和忍受就是最好的重构。我突然想到了cocos2dx,为什么明明它的功能愈发强大了,但是我对它的印象越来越差?作为3D引擎它连入场的资格都没有,而作为2D引擎,它越来越庞大、越来越厚重,像是老年迟暮一样。而且最恶心的是,它每次升级版本都会有不向下兼容的修改,cocos2dx和cocostudio也互不兼容。比如我使用cocosudio1.6,那么你最好不要使用v3的cocos2dx,乖乖使用v2的版本,因为不互相兼容。最新的cocos2dx使用FlatBuffer来替代ProtoBuffer,那么你的cocostudio也必须同步更新,否则无法加载配置,反之亦然。

虽然我暂时不会去动它,但是想法还是有的。维护一个统一的表,比如excel,里面描述好一个字段ID,显示的属性名字,值类型是什么,默认值是什么。然后程序加载这个表的数据来生成属性窗口和解析脚本内容。这样添加一个字段的时候只要在一个地方匹配好字段对应的表中字段ID就可以了。很眼熟不是吗? 其实游戏中的GUI都是这么玩的,通过一个配置生成UI内容。代码中注册回调来处理UI响应。我们这里是属性窗口,UI更加统一和一致,所以处理起来更加简单。

最后再提一个它蹩脚的设计,引以为戒。 它有一个enumToken的枚举,同时还有一个tokens的字符串数组,其顺序严格对应。程序中使用字符串的地方都使用tokens[枚举值]来索引。这个设计最蹩脚的地方在于字段数目很多的时候(现在有300个左右),你很难知道一个枚举具体对应哪个字符串。

修改方式,如果是c#中,直接定义一个类,其内部是public static readonly XXX = "xxx"。或者更近一步,直接使用枚举,C#中的枚举是可以转换为字符串的,当然这样需要枚举名就是写入脚本的字段名。

在c++中更加简单,直接使用#define XXX "xxx" 简单清晰无污染。

时间: 2024-12-29 07:02:31

实例说明什么是代码的坏味道,如何重构的相关文章

代码的坏味道

代码的坏味道Duplicated Code 重复代码Long Method 过长函数Large Class 过大的类Long Parameter List 过长参数列:类或者结构Divergent Change 发散式变化:一个类受多种变化的影响Shotgun Surgery 霰弹式修改:一种变化引发多个类相应修改.Feature Envy 依恋情结:一个类里的方法对另一个类的数据需求很大,需要移动方法的位置.Data Clumps 数据泥团:总是绑在一起出现的数据真应该拥有它们自己的对象.Pr

重构笔记——代码的坏味道(上)

在重构入门篇中,简单地介绍了重构的定义.为何重构.何时重构等.我想对于重构是如何运作的,你已经有了较好的理解了.但是对于代码中的坏味道,你可能 知道的并不多.坏味道可能是无形中产生的,也可能是开发人员偷懒造成的,还可能是其它某些因素导致的.不管怎么样,代码中的坏味道对程序没有半点好处,它 会促使程序腐烂,甚至变质.对于开发人员,真的是很有必要对这些坏味道进行了解和熟悉,理解它们产生的场景.针对当前程序,发现坏味道,对代码进行重构, 以消除坏味道,提高代码质量,提高自己水平. 下面让我们一起来熟悉

代码的坏味道之二——译自《重构》

巨型类 当一个类尝试做的太多,它常常展示出过多的实例变量.当一个类有太多实例变量,重复代码的出现就不远了. 你可以提取类来打包一部分变量.选择在部件中有意义的变量放在一起.例如,“存款总量”和“存款货币”很可能在同一部件中.更宽泛的说,在一个类中变量的某个子集共同的前缀和后缀预示着组成同一个部件的机会.如果这个部件有成为子类的意义,你会发现提取子类往往更容易. 有时一个类不会一直使用它全部的实例变量.如果如此,你可能可以提取类或者提取子类若干次. 相比于一个类有太多实例变量,一个类有太多代码是重

代码的坏味道之五 ——译自《重构》

夸夸其谈未来性Speculative Generality Brian Foote 为一个我们都很敏感的味道建议的名字.你会遇到它当有人说“哦,我认为我们某一天会需要能力去做那一类的事”然后这样一来希望得到各种钓钩和特别的例子去处理并不需要的事情.结果往往是更难懂也难维护.如果所有的这些机制被用上,那这样做还是值得的.如果不是这样,也就不值得.这个机制就是这样产生的,所以处理掉它. 如果你有抽象类并没有做很多事,用Collapse Hierarchy.不必要的委托可以用Inline Class去

代码的坏味道之四 ——译自《重构》

基本类型偏执Primitive Obsession 大多数编程环境有两种类型的数据.记录类型允许你把数据结构化成有意义的集合.基本类型是你建设用的砖块.记录类型总是会产生一定量的额外开销.这可能是数据库中的表,或者被很尴尬的创建当你希望他们只为一或两件东西存在. 关于对象一个很有意义的东西是,他们模糊甚至打破了基本类型和大型类之间的界线.你可以很轻松的写小的无法和语言中内建类型相区别的类.Java对数字有基本类型,但字符和日期这些在其他环境也是基本类型的,在Java里是类. 新接触对象的人通常不

代码的坏味道【4】

返回总目录 十四.Temporary Field(令人迷惑的暂时字段) 1.某个实例变量仅为某种特定的情况而设 2.某些实例字段仅为某个函数的复杂算法少传参数而设 将这些变量和相关函数提炼到一个独立的类中. 十五.Message Chains(过度耦合的消息链) 如果你看到用户向一个对象请求另一个对象,然后再向后者请求另一个对象,然后在请求另一个对象……这就是消息链. 实际代码就是一长串的getThis()或者一长串临时变量. 使用隐藏“委托关系”(这个后面会讲)来进行重构.当然了,可以在消息链

22种代码的坏味道,一句话概括

22种代码的坏味道,一句话概括: 假设一段代码是不稳定或者有一些潜在问题的,那么代码往往会包括一些明显的痕迹. 正如食物要腐坏之前,常常会发出一些异味一样. 我们管这些痕迹叫做"代码异味". 參考资料: http://blog.csdn.net/sulliy/article/details/6635596 http://sourcemaking.com/refactoring/bad-smells-in-code Code smells Duplicated Code --------

重构摘要3_代码的坏味道

如果尿布臭了,就换掉它. 1.Duplicated Code 重复代码 Extract Method Pull Up Method Form Template Method --> Template Method 模式 Substitute Algorithm --> 函数算法替代 2.Long Method 过长的函数 "间接层"所带来的全部利益--解释能力.共享能力.选择能力--都是有小函数支持的. 真正关键在于一个好名字. 每当感觉需要以注释来说明点什么的时候,我们就

代码的坏味道之三——译自《重构》

散弹式修改(Shotgun Surgery) 散弹式修改和发散式变化类似,但却相反.每当你做一种修改你却必须对很多不同的类做很多小的变化,你面临的就是散弹式修改.当变化到处都是时,有的变化就不好找到了,这样很容易漏掉重要的更改. 这种情况下你要使用移动方法(Move Method)和移动字段(Move Field)来把所有的变化放到一个类里.如果没有现成的类合适,就创建一个类.通常你会用到内联化类(Inline Class)把一系列行为放到一起.你会有一点发散式变化的问题,但你可以轻松处理它.