首页 / 软件开发 / JAVA / Java编程的动态性,第3部分: 应用反射
Java编程的动态性,第3部分: 应用反射2011-04-09 IBM Dennis M. Sosnoski命令行参数处理是一项令人厌烦的零碎工作,不管您过去已经处理过多少次了,它好像总 能重新摆在您的面前。与其一遍又一遍地编写同一块代码的不同变种,为什么不利用反射来 简化参数处理的工作呢?Java 顾问 Dennis Sosnoski 向您展示了如何做到这一点。在本文 中,Dennis 简明扼要地介绍了一个开源库,这个库可以使得命令行参数实际上自己处理自己 。在 上个月的文章中,我介绍了Java Reflection API,并简要地讲述了它的一些基本功能 。我还仔细研究了反射的性能,并且在文章的最后给出了一些指导方针,告诉读者在一个应 用程序中何时应该使用反射,何时不应该使用反射。在本月这一期的文章中,我将通过查看 一个应用程序来更深入地讨论这一问题,这个应用程序是用于命令行参数处理的一个库,它 能够很好地体现反射的强项和弱点。一开始,在真正进入编写实现代码的工作之前,我将首先定义要解决的问题,然后为这个 库设计一个接口。不过,在开发这个库的时候,我并不是按照上述步骤进行的――我先是尽 力简化一群有公共代码基础的应用程序中的现有代码,然后使之通用化。本文中使用的“定 义-设计-构建”这种线性序列比起完完整整地描述开发过程要简练得多,而且,按照这种方 式来组织对开发过程的描述,我可以修正我原先的一些假设,并清理掉这个库的代码中一些 不必要的方面。您完全有希望发现将上述方式作为开发您自己的基于反射的应用程序时所使 用的模型十分管用。定义问题我曾经写过许多使用命令行参数的Java应用程序。一开始,大多数应用程序都很小,但最 后有些应用程序却变得大到出乎我的意料。下面是我观察到的这些应用程序的变大过程的标 准模式:一开始只有一个或者两个参数,按照某种特定的顺序排列。考虑到这个应用程序有更多的事情要做,于是添加更多的参数。厌倦了每次都输入所有的参数,于是让一些参数成为可选的参数,让这些参数带有默认的 值。忘记了参数的顺序,于是修改代码,允许参数以任何顺序排列。将这个应用程序交给其他感兴趣的人。但是他们并不知道这些参数各自代表什么,于是又 为这些参数添加更完善的错误检查和“帮助”描述。当我进入到第5步的时候,我通常会后悔没有将整个过程都放在第一步来做。好在我很快 就会忘记后面的那些阶段,不到一两个星期,我又会考虑另外一个简单的小命令行程序,我 想拥有这个应用程序。有了这个想法之后,上述整个恶心的循环过程的重现只是时间的问题 。有一些库可以用来帮助进行命令行参数处理。不过,在本文中我会忽略掉这些库,而是自 己动手创建一个库。这不是(或者不仅仅是)因为我有着“非此处发明(not invented here )”的态度(即不愿意用外人发明的东西,译者注),而是因为想拿参数处理作为一个实例 。这样一来,反射的强项和弱点便正好体现了对参数处理库的需求。特别地,参数处理库:需要一个灵活的接口,用以支持各种应用程序。对于每个应用程序,都必须易于配置。不要求顶级的性能,因为参数只需处理一次。不存在访问安全性问题,因为命令行应用程序运行的时候通常不带安全管理器。这个库中实际的反射代码只代表整个实现的一小部分,因此我将主要关注与反射最相关的 一些方面。草拟出一份设计应用程序访问参数数据最方便的方式或许是通过该应用程序的 main 对象的一些字段。例 如,假设您正在编写一个用于生成业务计划的应用程序。您可能想使用一个 boolean 标记来 控制业务计划是简要的还是冗长的,使用一个 int 作为第一年的收入,使用一个 String 作 为对产品的描述。我将把这些会影响应用程序的运行的变量称作 形参(parameters),以便 与命令行提供的 实参(arguments)――即形参的值区分开来。通过为这些形参使用字段, 将使得在需要形参的应用程序代码中的任何地方都可以方便地调用它们。而且,如果使用字 段的话,在定义形参字段时为任意形参设置默认值也很方便,如清单1所示:清单 1.业务计划生成器(部分清单)public class PlanGen {
private boolean m_isConcise; // rarely used, default false
private int m_initialRevenue = 1000; // thousands, default is 1M
private float m_growthRate = 1.5; // default is 50% growth rate
private String m_productDescription = // McD look out, here I come
"eFood - (Really) Fast Food Online";
...
private int revenueForYear(int year) {
return (int)(m_initialRevenue * Math.pow(m_growthRate, year- 1));
}
...