最近我工作的主要内容,是在和别人结对编程,以对一个大型的遗留系统项目进行重构。
过程中,我发现一个特别有意思的东西,我重构了很多的 if 语句。从这些 if 语句里,大抵是映射出了业务的变化。于是,我便想写一篇文章来记录一下相关的心得。
业务的复杂性,导致了架构的复杂性。在这些代码故事里,发生得最多的地方就是 if 语句。所以,你可以从大部分的 if 语句里,看到一些代码上的坏味道。
你先写了一个 if 语句里面只有一个条件,没问题。但是后来的人,又加了一个条件,因为业务上确实需要这么做。于是,后来,又不得加了一个if 语句,导致了这个条件变得更加复杂。
if (isCondition && isNotASwitchCase && .... && ....) {
}
所以,完了,这些代码越来越难以维护。
于是,我们应对于这类条件判断,有两种做法: 提取变量 和 提取方法 。当你的判断条件是一个方法的时候,你可以想象一下它的架构是多么的复杂。
开始的人加了一个简单的条件判断,因为当时真的只有这么一种业务场景。你又不能过度设计,成一个 switch-case。但是,后来又多了好多个场景。
if (aCondition == "A") {
} else if (bCondition == "B") {
}
更不要提有人在每个 if 里写一个: if (myString.toUpperCase().equals(myOtherString.toUpperCase())) 。
针对于有限的 if 语句来说,可以转为 switch case(在 IDEA 里只需要 alt + enter 就可以自动完成)。
随着时间的推移我们的条件越来越复杂,我们的 if 语句会越来越复杂。
随着 if 条件进一步扩大化,我们的条件语句就变成了一个多层嵌套的循环语句。每多一层嵌套代码复杂度就 * 2,它的阅读难度就越来越大。于是乎:
if (condition) {
if (blabla) {
...
}
}
面对这一类 if 条件语句,我们能所做的就是:
诸如于:
if (!condition) {return}; // 为了演示方便
if(blabla){...}
又或者是诸如于三元表达式,不过我讨厌难以阅读的三元表达式——但是,只是 true 和 false 的情况下,还是相当不错的。
当业务进一步复杂化的时候,我们的 if 条件里就充斥着各种各样的逻辑。
if (conditionA) {
blablaA();
blaA(blabla).blabla();
}
我们的 if 方法随之变得越来越长,于是尝试去抽成一个方法。但是,当你又遇到一个新的场景时,你又加了一个 if 语句。后来,又又加了一个 if 语句。你才发现说,『咦,不对,这些 If 语句违反了开闭原则』。
于是,你尝试把代码重构成多态以替换 if 语句。
你开心的话,还可以转为 Factory + Strategy。
你开心的话,你也可以将它转为 HashMap 。
但是,在你写下第一个 if 的时候,你并不知道它会变成什么样的。所以,不要提前去把它转为这么复杂的架构。
如果你的业务场景真的超级复杂,那么你可能会看到一个非常长的 if 代码。它可能有几十个条件,有几百行到几千行的规模。
那么,你可以尝试使用 注册表模式 + 注解,通过反射的方式来重构你的 if 语句。
在你进一步修改代码之前,让我们来又双叕明确一下什么叫重构
重构(Refactoring)就是通过调整程序代码改善软件的质量、性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性。
换句话来说,重构只是在改善现有的代码,使其更易于阅读,换句话来说就是:Clean Code。而当我们说整洁的代码(Clean Code),说的是 易于理解、修改和测试 的。易于理解和修改意味着:
而易于理解的前提便是能 让每个团队成员快速理解 。(PS:当然了,若是有些人智商不够或者经验不够,他/她需要去需要去增强这方面的能力)。这便意味着,出于这样的目的,你不能编写过于抽象、简练的逻辑。而你又不能写得过于繁琐,充满大量地无用字符。
若是想使代码易于测试,则要先 使代码可测试 。而在这没有测试之前,我们是难以对代码进行大规模重构。所以,我们就陷入了一个死循环,没有测试,测试不了,没法重构。
等等,那我们为什么要进行重构呢?为了 ¥¥¥¥¥¥¥$$$$$$$$$ => 快速发布软件。
当软件是一个产品而不是一个项目的时候,我们就需要不断发布新功能,以满足客户的要求。而为了快速发布应用,我们需要让每次的改动最小,测试最少,才能实现快速发布。基于这样一个目标,我们会发现我们的诸多实践都是以此为出发点的。比如说,我们采用插件化、微服务化、组件化的方式,都是为了将软件的改动变小,这样一来,就减少了相应部分的测试工作,从某种意义上来说,就加快了软件发布的流程,从而更好的实现业务价值。因此,我们的第一步就是使 二进制改动最小 。而要做到二进制改动最小,那么我们就要做到 高内聚、低耦合 。
因此,不论是在编程还是在设计架构的时候,我们都要尽量满足 SOLID 五项原则中的:
既然,我们都已经知道了,如何去重构,如何用设计模式来解决问题。那么,我们会让我们的代码变得更好吗?不会,因为在流水线式的生产里,每个人都能找到合理的理由。
我们日常开发的模式是:红-绿-重构。而因为时间的原因,我们少去了重构这一步。
在上吊绳(deadline)的驱动下,我写了一这篇文章。尽管预先写好了文章的大纲,但是有很多字是打错的。
而对于真实的业务开发来说,要事先设计好相关功能的架构,意味着你得有充足的时间。这样一来说,你在大的方面上才不会犯错。可是呢,你真的有那么多的时间可以设计吗?你今天加的班,还好吗?
改动了你的代码,我就要负责。所以,我不去修改别人的代码。
没有测试,难以理解代码背后的业务原因。外加之组织文化,导致的沟通障碍;又或者是大家都很忙,没人愿意解释/回顾一下这一块的代码。
对,大部分的问题本质都是人的问题。
因为你只需要按下 IDEA 的快捷键,就能完成上面的大部分重构工作。当然了,需要有技巧的按,而不是像 Monkey 一样弹钢琴。
开心就好。
原文 http://www.phodal.com/blog/code-history-in-crime/
一个系统可以维持5年,10年,甚至20年以上,但是代码和设计模式的生命周期非常短,当对一个解决方案使用不同的方法进行迭代的时候,通常只能维持数月,数日,甚至几分钟的时间
良好的编程习惯涉及到很多方面,但在软件行业内,大多数的公司或组织都不会把良好的编程习惯列为主要关注点。 例如,具有可读性和可维护性的代码比编写好的测试代码或使用正确的工具更有意义,前者的意义在于可以让代码更易于理解和修改。
减少嵌套会让代码可读性更好,同时也能更容易的找出bug,开发人员可以更快的迭代,程序也会越来越稳定。简化代码,让编程更轻松!
Google为了那些还不熟悉代码规范的人发布了一个JS代码规范。其中列出了编写简洁易懂的代码所应该做的最佳实践。代码规范并不是一种编写正确JavaScript代码的规则,而是为了保持源代码编写模式一致的一种选择。
程序员似乎忘记了软件的真正目的,那就是解决现实问题。您编写的代码的目的是为了创造价值并使现有世界变得更美好,而不是满足您对自我世界应该是什么的以自我为中心的观点。有人说:如果你拥有的只是一把锤子,那么一切看起来都像钉子一样
TinyMCE是一个轻量级的基于浏览器的所见即所得编辑器,由JavaScript写成。它对IE6+和Firefox1.5+都有着非常良好的支持。功能方强大,并且功能配置灵活简单。另一特点是加载速度非常快的。
函数式编程对应的是命令式编程, 函数式编程的核心当然是对函数的运用. 而高阶函数(Higher-order)是实现函数式编程的基本要素。高阶函数可以将其他函数作为参数或者返回结果。所以JS天生就支持函数式编程
朋友发表了一条说说:入职新公司,从重构代码到放弃”,我就问他怎么了?他说,刚进一家新公司,接手代码太烂,领导让我先熟悉业务逻辑,然后去修复之前项目中遗留的bug,实在不行就重构
页面实现关键词高亮显示:在项目期间遇到一个需求,就是搜索关键词时需要高亮显示,主要通过正则匹配来实现页面关键词高亮显示。在搜索结果中高亮显示关键词:有一组关键词数组,在数组中筛选出符合关键字的内容并将关键字高亮
软件工程学什么? 学计算机,写程序,做软件,当程序员。听说学计算机很辛苦? 是的,IT行业加班现象严重。在计算机世界里,技术日新月异,自学能力是程序员最重要的能力之一。选了这个专业,就要时刻保持好奇心和技术嗅觉,不能只满足于完成课内作业。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!