通用方法接口

本文通过一个Java类求和的实例,探讨如何使用接口、代理模式等技术进行代码重构,以减少重复逻辑并提高代码的可维护性和扩展性。

转:

作者正文
ajoo

 

这里讲述的是一个非常让人尴尬的故事

我们有一个简单的java类:

代码
  1. class Details {   
  2.   double getBalance();   
  3.   double getFixed();   
  4.   double getVariable();   
  5.   double getSpendDown();   
  6.   ...   
  7.   //各种getter以及其他相关的逻辑   
  8. }   

<script>render_code();</script>

 

现在业务逻辑需要对一些property做求和操作,求overallBalance, overallFixed之类的。
没什么了不起的,一个for循环分分钟搞定:

代码
  1. static double getOverallBalance(Details[] arr){   
  2.   double sum = 0;   
  3.   for(int i=0; i<arr.length; i++) {   
  4.     sum += arr[i].getBalance();   
  5.   }   
  6. }   

<script>render_code();</script>

 

同理,对overallFixed,代码大同小异,copy-paste先。

代码
  1. static double getOverallFixed(Details[] arr){   
  2.   double sum = 0;   
  3.   for(int i=0; i<arr.length; i++) {   
  4.     sum += arr[i].getFixed();   
  5.   }   
  6. }   

<script>render_code();</script>

 

这都没什么。可是当我写到第七个getOverallBlahBlah(arr)函数的时候,终于有点受不了了。这代码重复的虽然不多,但是架不住这么没完没了阿。

作为code-against-interface的推崇者,作为一个函数式编程的扇子,最自然的想法就是把不同的getter逻辑抽象成一个Getter接口,如下:

代码
  1. interface Getter {   
  2.   double get(Details details);   
  3. }   
  4. static double sum(Details[] arr, Getter getter){   
  5.   double sum = 0;   
  6.   for(int i=0; i<arr.length; i++) {   
  7.     sum += getter.get(arr[i]);   
  8.   }   
  9. }   

<script>render_code();</script>
娜爱思啊。有比这代码更优雅的么?

 

然后各个求和的代码变成:

代码
  1. double overallBalance = sum(details, new Getter(){   
  2.   public double get(Details details){   
  3.     return details.getBalance();   
  4.   }   
  5. });   
  6. double overallFixed = sum(details, new Getter(){   
  7.   public double get(Details details){   
  8.     return details.getFixed();   
  9.   }   
  10. });   
  11. ....   

<script>render_code();</script>
嗯。几乎没有什么重复的逻辑了。

 

不过......
数数代码行数,怎么没有减少,反而略有盈余?仔细找找。发现原来的for loop是四行,现在的new Getter(){...}居然也是四行!!!
再加上一个sum()函数,我辛苦了半天的重构,居然代码行数增加了!

如果世界上有比一个java的匿名类的语法更臭的,那大概就是两个匿名类语法了。据说居然还有人质疑java 7引入closure语法的意义?

另一个方法是用apache commons beanutils的getProperty(),最终的语法会是:

代码
  1. double overallBalance = sum(details, "balance");   

<script>render_code();</script>
语法足够简单了,但是重构的时候就麻烦了,也没有code-completion可用。

 

尴尬阿。这么一个简单的for loop,用匿名类重构似乎不值得。但是就任由这七个(也许回头还会更多)长得一模一样的for loop这么站在这气我?

走投无路,开始琢磨奇技淫巧了。

先声明一个接口,来包含所有需要sum的property getter。

代码
  1. private interface IDetails {   
  2.   double getBalance();   
  3.   double getFixed();   
  4.   double getVariable();   
  5.   double getSpendDown();   
  6.   ...   
  7.   //所有其它需要做sum的getter   
  8. }   

<script>render_code();</script>

 

然后让Details实现IDetails。Details的代码不用变。

代码
  1. class Details implements IDetails {   
  2.   ...   
  3.   //代码不变   
  4. }   

<script>render_code();</script>

 

戏肉来了。写一个dynamic proxy,来封装sum逻辑。

代码
  1. static IDetails sumOf(final IDetails[] arr){   
  2.   return (IDetails)Proxy.newProxyInstance(   
  3.     getClass().getClassLoader(), new Class[]{IDetails.class}, new InvocationHandler(){   
  4.     public Object invoke(Object proxy, Method method, Object[] args)   
  5.     throws Throwable {   
  6.       double sum = 0;   
  7.       for(int i=0; i<arr.length; i++) {   
  8.         sum += ((Double)method.invoke(arr[i], args)).doubleValue();   
  9.       }   
  10.       return new Double(sum);   
  11.     }   
  12.   });   
  13. }   

<script>render_code();</script>

 

好了,接下来求sum的语法可以被简化为如下:

代码
  1. double overallBalance = sumOf(arr).getBalance();   
  2. double overallFixed = sumOf(arr).getFixed();   
  3. ...   

<script>render_code();</script>

 

而且,再需要sum新的property,只需要把这个getter放进IDetails接口,就大功告成了。

很有趣的dynamic proxy应用。不过,一个求和这么简单的事情居然要动用这种奇技淫巧,很值得自豪么?

要是在ruby里,我就直接:

代码
  1. sum(arr){balance}   

<script>render_code();</script>

 

该死的java啊!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值