转:
作者 | 正文 |
---|---|
ajoo
|
这里讲述的是一个非常让人尴尬的故事
我们有一个简单的java类:
- class Details {
- double getBalance();
- double getFixed();
- double getVariable();
- double getSpendDown();
- ...
- //各种getter以及其他相关的逻辑
- }
<script>render_code();</script>
现在业务逻辑需要对一些property做求和操作,求overallBalance, overallFixed之类的。
没什么了不起的,一个for循环分分钟搞定:
- static double getOverallBalance(Details[] arr){
- double sum = 0;
- for(int i=0; i<arr.length; i++) {
- sum += arr[i].getBalance();
- }
- }
<script>render_code();</script>
同理,对overallFixed,代码大同小异,copy-paste先。
- static double getOverallFixed(Details[] arr){
- double sum = 0;
- for(int i=0; i<arr.length; i++) {
- sum += arr[i].getFixed();
- }
- }
<script>render_code();</script>
这都没什么。可是当我写到第七个getOverallBlahBlah(arr)函数的时候,终于有点受不了了。这代码重复的虽然不多,但是架不住这么没完没了阿。
作为code-against-interface的推崇者,作为一个函数式编程的扇子,最自然的想法就是把不同的getter逻辑抽象成一个Getter接口,如下:
- interface Getter {
- double get(Details details);
- }
- static double sum(Details[] arr, Getter getter){
- double sum = 0;
- for(int i=0; i<arr.length; i++) {
- sum += getter.get(arr[i]);
- }
- }
<script>render_code();</script>
娜爱思啊。有比这代码更优雅的么?
然后各个求和的代码变成:
- double overallBalance = sum(details, new Getter(){
- public double get(Details details){
- return details.getBalance();
- }
- });
- double overallFixed = sum(details, new Getter(){
- public double get(Details details){
- return details.getFixed();
- }
- });
- ....
<script>render_code();</script>
嗯。几乎没有什么重复的逻辑了。
不过......
数数代码行数,怎么没有减少,反而略有盈余?仔细找找。发现原来的for loop是四行,现在的new Getter(){...}居然也是四行!!!
再加上一个sum()函数,我辛苦了半天的重构,居然代码行数增加了!
如果世界上有比一个java的匿名类的语法更臭的,那大概就是两个匿名类语法了。据说居然还有人质疑java 7引入closure语法的意义?
另一个方法是用apache commons beanutils的getProperty(),最终的语法会是:
- double overallBalance = sum(details, "balance");
<script>render_code();</script>
语法足够简单了,但是重构的时候就麻烦了,也没有code-completion可用。
尴尬阿。这么一个简单的for loop,用匿名类重构似乎不值得。但是就任由这七个(也许回头还会更多)长得一模一样的for loop这么站在这气我?
走投无路,开始琢磨奇技淫巧了。
先声明一个接口,来包含所有需要sum的property getter。
- private interface IDetails {
- double getBalance();
- double getFixed();
- double getVariable();
- double getSpendDown();
- ...
- //所有其它需要做sum的getter
- }
<script>render_code();</script>
然后让Details实现IDetails。Details的代码不用变。
- class Details implements IDetails {
- ...
- //代码不变
- }
<script>render_code();</script>
戏肉来了。写一个dynamic proxy,来封装sum逻辑。
- static IDetails sumOf(final IDetails[] arr){
- return (IDetails)Proxy.newProxyInstance(
- getClass().getClassLoader(), new Class[]{IDetails.class}, new InvocationHandler(){
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- double sum = 0;
- for(int i=0; i<arr.length; i++) {
- sum += ((Double)method.invoke(arr[i], args)).doubleValue();
- }
- return new Double(sum);
- }
- });
- }
<script>render_code();</script>
好了,接下来求sum的语法可以被简化为如下:
- double overallBalance = sumOf(arr).getBalance();
- double overallFixed = sumOf(arr).getFixed();
- ...
<script>render_code();</script>
而且,再需要sum新的property,只需要把这个getter放进IDetails接口,就大功告成了。
很有趣的dynamic proxy应用。不过,一个求和这么简单的事情居然要动用这种奇技淫巧,很值得自豪么?
要是在ruby里,我就直接:
- sum(arr){balance}
<script>render_code();</script>
该死的java啊!