精益求精,抑或得过且过

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

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

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

  阅读代码,我明白了原因。在原来的实现中,由于默认不同分数段有不同名次,因此,在获取这些分数段的值时,是将它们放入到一个Map<Subject, Map<Integer, Double>>中。这个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<Integer, Double>>的类和方法有很多,特别让人烦恼的是在这些方法中,有很多还定义在某些公共类中,被系统的大多数Client所调用。例如这样的代码:

public static JFreeChart createEliteTotalChart(Grade grade, String partial,
ExamSet es, Student stu, Map subToStuTotalMap,
Map
<Subject, Map<Integer, Double>> 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);

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

it知识库精益求精,抑或得过且过,转载需保留来源!

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。