程序员面临的最痛苦之事,莫过于修改旧代码;如果还有比这更痛苦的,就是修改糟糕透顶,乱得一团糟的烂代码。最近因为手底下一帮程序员都在忙,市场部正好又反馈过来一个要命的bug,一时手痒,就领下了这个任务。我们这个产品是针对教育行业的,它是在好几年前开发,然后不断完善和维护。这些阶段都是在我来到这家公司之前完成的。所以,我对于产品的代码并不熟悉。

原来的需求是假定客户设置分数段时,不同的分数段有不同的有效分,对应着也就有不同的名次。这些数据都是经过分析器分析获得,并持久化到数据库中。当我们需要生成学生报告时,再从数据库中获取,并将数据填充到iReport设置好的模板中,一个是二维表,一个是柱状和曲线图。

现在,我们发现某些学校需要给不同的分数段设置完全相同的有效分,以及相同的名次。报告打印出来,二维表没有错,曲线图却出现了“缺斤少两”的现象。例如设置五个分数段,却可能只显示了四条曲线。

阅读代码,我明白了原因。在原来的实现中,由于默认不同分数段有不同名次,因此,在获取这些分数段的值时,是将它们放入到一个Map<Subject, Map&lt;Integer, Double>&gt;中。这个Map是根据科目进行分类的,子Map的key值为Integer类型,其实就是分数段对应的名次,value则是设置的有效分。由于现在的名次存在重复,导致Map中的元素存在偏差。这就是五个分数段只显示四条曲线的缘由。

事实上,在我看到这样的Map时,就明白这种“超级强大”的容器,事实上往往会成为坏代码的泥沼。当我们将这样的对象作为参数,在方法之间传来传去的时候,会带来诸多问题:
1)性能影响。这种可能比较庞大的容器对象,有可能会形成性能瓶颈;
2)强类型。虽然这里使用了泛型,但泛型类型却使用了基本类型;
3)封装性不够好。这样的容器对象暴露了太多的数据细节,且不利于为其定义职责行为。
4)可读性差。看到这样的Map,你并不会在第一时间明白它到底存放了什么。
5)可扩展性差:当这个Map作为方法的参数时,相当于这个参数没有被对象化。如果拥有这个参数的方法被公开,且广泛调用,一旦需要改变参数,牵连到的代码就会非常多。

事实正是如此。当我在分析我们的遗留代码时,发现很多地方都在重复获取这个Map对象,并且这个Map对象也在多个方法之间传递。因为这种容器对象自身的缺陷,为我的bug修复带来了很多阻碍。要解决这个Bug,就不能再将名次作为Map的key值。查看相关的数据表,事实上我们还给出了一个分数段的名称。当名次和有效分存在重复的情况下,结合分数段名称就能确定唯一值。因此,一个简单地做法就是将Map的key修改为名次加名称的组合,即将Integer修改为String。

现在,我需要决策。如果希望一劳永逸,就应该摒弃这种Map的做法。我们应该利用封装来实现这一目标。例如定义ValidScore和ValidScoreRange。后者相当于之前的Map<Integer, Double>。ValidScore则包含属性:Rank, LineName, Score。事实上,ValidScoreRange正是ValidScore的集合。我们还可以在ValidScore和ValidScoreRange中定义许多与该领域相关的行为。通过这样的封装,问题会变得简单许多。

然而,思考良久,我却放弃了这个符合OO原则的做法。原因在于,遗留代码中使用Map<Subject, Map&lt;Integer, Double>&gt;的类和方法有很多,特别让人烦恼的是在这些方法中,有很多还定义在某些公共类中,被系统的大多数Client所调用。例如这样的代码:

public static JFreeChart createEliteTotalChart(Grade grade, String partial,
ExamSet es, Student stu, Map subToStuTotalMap,
Map<Subject, Map&lt;Integer, Double>&gt; subToValidScoreMap,
List subList,long stuRank,
String[] validLineSeries,double barWidth,Color barColor) {
if (subToStuTotalMap == null || subList == null) {
return null;
}
Color[] color = {Color.RED,Color.BLUE,Color.GREEN,
Color.CYAN,Color.BLACK,Color.MAGENTA};
CategoryDataset[] dataset = EIDatasetFactory.createEliteTotalDataset(
subToStuTotalMap, subToValidScoreMap, subList,validLineSeries);

//......为清晰起见,其余代码略
}

注意,在createEliteTotalChart()方法中,调用了EIDatasetFactory的createEliteTotalDataset(),对Map<Subject, Map&lt;Integer, Double>&gt;对象进行了处理。EIDatasetFactory正是一个公开的公共类,createEliteTotalDataset()方法也是一个被广泛调用的方法。

好吧,我们可以利用重构来完成代码的改造。但是我现在不敢轻易修改这些方法。因为我并不知道这些方法除了生成有效分报告外,还有哪些功能会调用。我们当然可以通过寻找引用来识别到底有哪些功能会调用,事实上,我找到了好几个引用。可是我依旧需要保持足够的谨慎。究其原因,是之前的遗留代码并没有提供充分的单元测试,无法通过单元测试用例来保证这一修改的正确性。

好吧。我得承认重构本来就离不开单元测试,除非是那些直接可以利用工具就能完成的重构。问题不是我不能重构,也并非我的能力达不到;问题也不是我无法保证修改的正确性;真正的问题是我没有时间。作为公司的技术领导,我可以向CEO争取这个时间吗?不能!因为CEO必须考虑成本,他要考虑花这么多时间去做看似不能增加功能的重构是否值得。或许你会说,做这样一个小小的重构需要花太多时间吗?问题是在这个遗留系统中,这样的代码简直数不胜数。所以,在这里时间才是真正的罪恶。不过,最大的罪恶还是人,是编写代码的人。如果在一开始开发系统的时候,我们就能注意代码质量,随时重构,并遵循面向对象设计原则,也许境况会变得焕然一新。这就是所谓的“破窗理论”了。从中可见,代码质量对系统架构的影响。

面对这样一座满是破窗的大楼,我们是下定决心推倒重建,还是修修补补维持现状?这实在是一个哈姆雷特似的问题。站在技术人员的角度,我倾向于推倒重建;然而站在公司领导的角度,却不得不思考成本与时间。

因为这个遗留系统还可以苟延残喘,因为我的时间不允许停留在这个遗留系统太久,我还有新产品的架构工作要做。最终我选择了“得过且过”。我针对这一特定需求,找到了调用的方法。为了不影响其他功能,我利用Copy&Paste的方式复制了该方法,只是为它换了一个名字。当然,我还可以利用Extract Method重构来尽量避免代码的重复。然后,将Map<Subject, Map&lt;Integer, Double>&gt;对象修改为Map<Subject, Map&lt;String, Double>&gt;。因为该方法又连续调用多个其他方法,传递了Map<Subject, Map&lt;Integer, Double>&gt;对象,我又如此炮制地复制了这些被调用方法,最终解决了这个Bug。可是看到这么多Copy&Paste的重复代码,我不寒而栗,决定仓皇逃走。在时间这个强大敌人的面前,我悲哀地选择了投降。

转载于:https://blog.51cto.com/wayfarer/495892

精益求精,抑或得过且过相关推荐

  1. 特斯拉员工自曝:为应付产能不惜偷工减料,出厂测试得过且过

    郭一璞 发自 凹非寺  量子位 报道 | 公众号 QbitAI 马斯克和他的特斯拉,不断被员工拉下神坛. 应付产能地狱,全靠偷工减料:工作环境恶劣,员工饱受摧残. 表面光鲜的特斯拉,背后竟然如此不堪, ...

  2. 关于数据准确性,精益求精,神策数据矢志不渝的坚持

    你是否遭遇过以下场景? 老板:"你提交的报告,怎么和我查的不一样?" 业务部:"ERP 后台显示成单 687 笔,你怎么告诉我成单 620 笔?" 运营:&qu ...

  3. 怎样学习(3):迭代学习,精益求精

    古人云「十年寒窗无人问.一举成名天下知」,这是中国古代为数不多的读书人的真实写照.大多数读书人仅仅有十年寒窗,却不见得成名. 在软件开发领域有瀑布模式的软件project方法论.它将软开发的几个过程「 ...

  4. 第46课 精益求精 《小学生C++趣味编程》

    /* 第46课 精益求精 试编一程序,把分数1/7转化成小数的形式, 要求计算幸福空间上精确到小数点后100位. */ #include<iostream> using namespace ...

  5. les物流执行系统_【精益运营】细化运营 精益求精 将“精准物流”落到实处...

    随着汽车行业进入并驱争先的阶段,市场上的竞争日趋激烈,汽车物流服务也面临更加严峻的考验.2019年诸城分公司生产物流紧紧把握客户满意及精益物流两个主方向,从业务实际出发,从自身工作做起,注重每一个工作 ...

  6. 任正非:华为要防止内卷 精益求精不叫内卷

    6月26日,华为心声社区公开<任总与2020年金牌员工代表座谈会上的讲话>,讲话时间为5月8日.任正非在讲话中提到:华为需要多基因融合突变防止内卷. 任正非强调:工程领域要精益求精,这不叫 ...

  7. 魅族16s Pro体验:精益求精的打磨升级款

    作者 | 段嘉祺 出品 | 网易手机&数码<易评机>栏目组(油管&B站:惊奇科技 抖音:JQ163) 魅族16s Pro,2699元起的骁龙855Plus旗舰来了.似乎,嗅 ...

  8. 如何学习(3):迭代学习,精益求精

    古人云「十年寒窗无人问,一举成名天下知」,这是中国古代为数不多的读书人的真实写照.大多数读书人只有十年寒窗,却不见得成名. 在软件开发领域有瀑布模式的软件工程方法论,它将软开发的几个过程「需求分析,概 ...

  9. 五子棋项目结束总结_朱建锋:五子棋对局要不断总结、精益求精!

    原标题:朱建锋:五子棋对局要不断总结.精益求精! [编者按]这是朱建锋老师在2010年全国五子棋公开赛上的对局自我点评,从中我们不仅可以学习到一流棋手的行棋思路和中局作战能力,更能学到优秀棋手那种不断 ...

  10. 精编,精讲,精练,精益求精---AP微积分第10版在精雕细琢反复打磨中与你相遇

    精编,精讲,精练,精益求精-AP微积分第10版在精雕细琢反复打磨中与你相遇 AP微积分是美国大学38个专业的必修与选修课程,登陆中国以来,发展迅猛,汹涌澎湃,几乎成为高中留学生必选的课程,然而AP微积 ...

最新文章

  1. Codeforces 338D 对线性同余方程组的一点理解
  2. 可扩展标记语言--XML
  3. WINCE基于MC8630的3G模块的控制和加载
  4. day16——函数式编程和内置函数
  5. IDEA下svn配置与share project
  6. 如何取消 DiscuzX 帖子被系统自动隐?
  7. 王琦:5G新视频应用的机会窗口将在2-3年内
  8. linux CentOS 系统下如何将php和mysql命令加入到环境变量中
  9. VS Code创建C项目
  10. MacOS如何控制应用对麦克风和日历日程的访问
  11. python地理空间分析——构建SimpleGIS
  12. 一元一次方程用计算机怎么解,一元一次方程怎么解 [解一元一次方程的基本步骤]...
  13. 汽车控制器软件开发模式调研
  14. html圈小猫小游戏
  15. 蜗居(露骨争议电视剧《蜗居》...
  16. 二.英语语法 - 并列句
  17. 服务器电脑上保存的文件找不到了,保存在电脑上的word文件突然找不到怎么办?该如何找回来...
  18. 如何通过TikTok联盟营销赚钱
  19. 数据预处理之特征选择(Feature Selections Methods)
  20. C语言实现二、十、十六进制的相互转换

热门文章

  1. 283.移动零 (力扣leetcode) 博主可答疑该问题
  2. oracle的成本核算,Oracle11gR2全表扫描成本计算(工作量模式-workload)
  3. java反射 泛型类型_【译】9. Java反射——泛型
  4. 设有一头小母牛,从出生第四年起每年生一头小母牛,按此规律,第N年时有几头母牛?...
  5. WebSocket 时时双向数据,前后端(聊天室)
  6. Python中的strip(),lstrip(),rstrip()的用法
  7. ubuntu备份与恢复
  8. C#抓取网页数据、分析并且去除HTML标签(转载)
  9. 一个查询语句各个部分的执行顺序
  10. 事务、事务的隔离级别、一致性