`
ajoo
  • 浏览: 453131 次
社区版块
存档分类
最新评论
阅读更多
这里讲述的是一个非常让人尴尬的故事

我们有一个简单的java类:
class Details {
  double getBalance();
  double getFixed();
  double getVariable();
  double getSpendDown();
  ...
  //各种getter以及其他相关的逻辑
}


现在业务逻辑需要对一些property做求和操作,求overallBalance, overallFixed之类的。
没什么了不起的,一个for循环分分钟搞定:
static double getOverallBalance(Details[] arr){
  double sum = 0;
  for(int i=0; i<arr.length; i++) {
    sum += arr[i].getBalance();
  }
}


同理,对overallFixed,代码大同小异,copy-paste先。
static double getOverallFixed(Details[] arr){
  double sum = 0;
  for(int i=0; i<arr.length; i++) {
    sum += arr[i].getFixed();
  }
}


这都没什么。可是当我写到第七个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]);
  }
}

娜爱思啊。有比这代码更优雅的么?

然后各个求和的代码变成:
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();
  }
});
....

嗯。几乎没有什么重复的逻辑了。

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

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

另一个方法是用apache commons beanutils的getProperty(),最终的语法会是:
double overallBalance = sum(details, "balance");

语法足够简单了,但是重构的时候就麻烦了,也没有code-completion可用。

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

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

先声明一个接口,来包含所有需要sum的property getter。
private interface IDetails {
  double getBalance();
  double getFixed();
  double getVariable();
  double getSpendDown();
  ...
  //所有其它需要做sum的getter
}


然后让Details实现IDetails。Details的代码不用变。
class Details implements IDetails {
  ...
  //代码不变
}


戏肉来了。写一个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);
    }
  });
}



好了,接下来求sum的语法可以被简化为如下:
double overallBalance = sumOf(arr).getBalance();
double overallFixed = sumOf(arr).getFixed();
...


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


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

要是在ruby里,我就直接:
sum(arr){balance}


该死的java啊!
分享到:
评论
64 楼 CrayEye 2007-04-03  
楼主的作法很巧妙 但是还是不喜欢。 怎么说呢 我不太会表述 不自然吧。如果让我们用自然语言去描述的话肯定不会这么说的。 sumOf(arr).getXXXX 

我的理解 这是一个aop。 sumOf 截获Idetails接口中的getXXXX函数 改变其语意 使其变成了求和函数。

aop不应该是这么用的吧。而且就使用者来说 调用一个本来是取属性值的getXXXX函数却可以得到sum值。会让人感觉很magic 很不容易理解。

ruby应该是类似把属性放入hashmap的做法 而不是楼主的方法二Getter吧。



顺便问 如果在C++里遇到这一问题 最自然的想法应该是用宏吧?
63 楼 dovecat 2007-04-02  
ray_linn 写道
说实话 Dy那个什么proxy十分的丑陋,是我见过的最丑的东西之一. 而且proxy这个词又十分含糊,鬼知道这里突然出现这个proxy,是什么原因什么由来,是不是天外又飞仙了?

这种需求,反射是我马上能想到的东西,

个人觉得EastSun的代码,兼顾的简洁和易读二者的平衡.LZ那个代码换个人,估计就得浪费一个早上去看堆资料,而且行数比反射只多不少.

那种感觉,真的就象柳永的词,卖弄的成分多,实用的成分少.


反射的话,比dyna proxy要更加清楚明确.
不过,话说回来,"酒窝"这东西还是挺可爱的.
62 楼 samhe 2007-04-02  
恩,一直都想搞个类似的东西.哈哈...现在有现成的了.
61 楼 jellyfish 2007-03-04  
java.lang.Number
60 楼 jianfeng008cn 2007-03-03  
又看了一下,感觉对一个程序员来讲,楼主的思路应该很容易理解才合理,至于涉及架构什么的,一般人我还用不到它。

引用

具体实现代码不写了,熟悉beanutils的都知道,不熟悉的,看看javadoc也就明白了(顺便说一下,不要什么东西都动不动就用“学习”两个字,beanutils也要学?pico也要学?是不是街角新开的洗脚房也需要“学习”一下?很让我有一种一个带着眼睛的三十岁左右男子,在和小姐commit transaction之前,在屋里到处细细索索地翻找东西,小姐问说你找什么?回答说要找到manual或者tutorial或者21天精通之类的书先“学习”一下的联想。)


说得过分了,庸俗了,看javadoc就不是学习拉
59 楼 shiwentao1982 2007-03-03  
没用过这种技巧,学习!
58 楼 dearmite 2007-03-03  
method.invoke(arr[i], args)).

Details.class.getMethod(name);  
其实思路是差不多的,都是得到你的类的method.
但是第一种可以用类来传递,
第二种就只可以用字符串来传。

为什么大家对第二种感觉不错,
更有人用JS来弄。

而对楼主的第一种思路不感冒呢?
请大家拿出当初学习getMethod的这种精神来重新审视一下楼主的代码。
和自己是否对新生的JAVA写法有些抵触。

我和楼主的感觉是差不多的。
要么使用for .
要么使用代理。

这两种都有一个共性就是可以重构。



57 楼 dearmite 2007-03-03  
dreamstone 写道
看了文章感觉很有意思,作者的思维挺好玩,感觉如果平时应用开发还是不要这样的好,如果是写一些特殊的需求,例如框架什么的还是不错的。另外再提供一个思路:
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("JavaScript");
for(int i=0;i<arr.length;i++){
    engine.put("obj", arr[i]);
    Object obj = engine.eval("obj."+method);
    if (obj instanceof Double){
        sum += ((Double)obj).doubleValue();
    }
}

是用jdk1.6中的script就可以简单实现了,好处是写起来方便,学习成本低。坏处是也存在重构问题,并且内部实现和动态代理、反射等也差不哪里去。


利用JS的字符串与类的弱混淆来达到把类来“数组化”这一点。
好象感觉上还没有
getMethod("")来的直观些呢。
这种方法猛滴一看,
以为进了JSP的JAVA代码里了呢?

再说,这种代码的效率利用JS的engine,高不到哪去吧?

我上面说了,不是财务系统,通用化的不值。
如果是财务系统。
用脚本来替JAVA语言,
大哥,你还是直接杀了我算了。

我搞过一段时间的性能测试的。这样的做法会让我crazy

56 楼 dearmite 2007-03-03  
ajoo 写道
继续我们的奇技淫巧之旅。




double totalBalance = sumOf(accountArray, "balance");
double totalRate = sumOf(accountArray, "rate");
double totalReturnOfInvestment = sumOf(accountArray, "returnOfInvestment");



4。于是,就出现了我们慕容家的“斗转星移”神功了。就像做梦一样,你可以这样写了:
double totalBalance = sumOf(accountArray).getBalance();
double totalRoi = sumOf(accountArray).getReturnOfInvestment();

作为一个客户,我几乎无法挑出什么毛病了,所有的Eclipse提供的拐棍都还在,语法也是简练自然得象自然语言。




感觉不错,
其实什么是“模式”有两个特点,
一个就是应用起来简单一些,
一个就是适用比较广。
这两点,
楼主的技巧都具备了。第一是可以重构。使用字符串的重构尽管有选项,但是大家都知道是怎么回事。
尤其是用过hibernate的。
那么,不管这个技巧本身多复杂,挺多是这个JAVA的JAVADOC很长,很长而已。
你又不用改变这个JAVA文件,
为什么大家就不认同这个方法呢,
不知大家看过JBOSS的一些常规包的JAVA文件没。

那上面的代码就不“花”么?我想比楼主的还要再花上一套吧?
不管怎么说,
先是赞楼主一个。

但是,话说回来,如果项目里只有3处以下的类要用到这个求和。
我还是喜欢for
但是,要真的是个财务系统。
有300个这种求和呢?
不光是我不喜欢for了吧?
那公司里正好有一个人写了楼主这么“优美”的“模式”,但是只给你一个JAR包。没有JAVA文件
而且此人正是你所**才可见的高人之一,

你可能巴不得把他的代码反编译过来,然后研究而后快呢。

呵呵。


建议楼主把此JAVA文件,打成JAR包,但是JAR包里面放上一份TXT说明再放上一个JAVA文件。
JAR包的名字就叫 financeSum.jar

并冠以公司财务系统高级架构包一员的美名。

那么研究之人肯定比现在要多。
:)


55 楼 glassprogrammer 2007-03-02  
huhu, 我也赞成第二种方法是标准解耦的方法. 不过奇淫技巧也是很有价值的, 人类历史上的重大发明, 开始的时候还不都是奇淫技巧.
54 楼 intolong 2007-03-01  
第二种方法是标准解耦的方法。其他的才是奇淫技巧
53 楼 yyzheng 2007-03-01  
ajoo 写道
yyzheng 写道
呵呵 我觉得楼主在最后一贴的“疯狂”是有道理的,当整个系统中大部分业务处理逻辑都是“求和”的情况下,用一种方式将这种逻辑抽象出来我认为也不为过,只不过他用的技巧有点让人眼晕,不过没关系,因为一个团队里总会出一两个这样的“疯子”,让这些“疯子”维护这种代码即可,从架构的角度上看,楼主明显已经抽象出一层业务处理逻辑(hoops...难不成这就是传说中的业务平台。。。)
从design的方向上,我赞成楼主的观点,但从design的技巧上我不认同楼主的技巧,why:因为楼主利用了原有设计上的一个缺陷,就是对每一个class原来的设计都强迫其实现一个接口,这种取巧的方式不利于以后architecture的重构,而且楼主为了让没有实现接口的class也能用到重构后的模块,又新加了一些特定的接口,而这些接口是没有任何业务语义的,仅仅是为了用到重构后的模块而加的(hoops...难不成这就是传说中的侵入。。。)好吧,如果你想让按楼主重构后的架构clean一些的话(我承认我有一些技术洁癖),又需要再付出努力组织这些东东,而这样的代价是否值得呢,这实际上就是在重新架构你的system。。。


理解万岁。呵呵。

不过,这些新的接口是不要求被sum的类来"implements"的。因为“酒窝”可以搞定这一切。所以,不能算“侵入”吧?


哦,我又看了一下,可以不用在原先系统架构的那些废接口上做文章,直接添加新的接口即可,这样的话我说的:“因为楼主利用了原有设计上的一个缺陷,就是对每一个class原来的设计都强迫其实现一个接口,这种取巧的方式不利于以后architecture的重构”就不成立了,看来“酒窝”是个好东东,有时间要好好研究一下:)

我这里说的侵入含义比较广,不仅仅是强迫应用类实现某些特定的接口或继承某些特定的基类,当然,按照这种概念,只要你用到了某种框架,其实都或多或少算作“侵入”了。但作为一个框架,提供给应用层的接口很重要,好的框架提供的侵入方式很自然,就像spring。

新增的这些接口仅仅是为了用到重构后的模块而加的,体现的是一种框架的配置语义,这样的话怎么组织这些东东以提供更clean的接口给团队其他程序员就很重要了,不然其他人会抱怨的:)

其实我觉得你提出的第二种Getter接口的方法就已经很好了,面向接口、简单实用、在架构上也很清晰,做应用的话写到这种程度就已经足够了,匿名内部类其实也很简单,而且有编程工具的支持,虽然代码行数没少多少,但从整体架构上更清楚了。
52 楼 intolong 2007-03-01  
whyang 写道
搞那么复杂干啥

看很简洁

static double getOverallBalance(Details[] arr){   

      Details sumDetails=new Details();
 
      for(int i=0; i<arr.length; i++) {   
        sumDetails.setBalance()=sumDetails.getBalance()+arr[i].getBalance();   
        sumDetails.setFixed()=sumDetails.getFixed()+ arr[i].getFixed();   
        sumDetails.setVariable()=sumDetails.getVariable()+ arr[i].getVariable();   
        sumDetails.setSpendDown()=sumDetails.getSpendDown()+ arr[i].getSpendDown();   
      }   
} 



人家只想要getBlance(),你给这老多
51 楼 whyang 2007-03-01  
搞那么复杂干啥

看很简洁

static double getOverallBalance(Details[] arr){   

      Details sumDetails=new Details();
 
      for(int i=0; i<arr.length; i++) {   
        sumDetails.setBalance()=sumDetails.getBalance()+arr[i].getBalance();   
        sumDetails.setFixed()=sumDetails.getFixed()+ arr[i].getFixed();   
        sumDetails.setVariable()=sumDetails.getVariable()+ arr[i].getVariable();   
        sumDetails.setSpendDown()=sumDetails.getSpendDown()+ arr[i].getSpendDown();   
      }   
} 
50 楼 sandy 2007-03-01  
这种情况,还是借助于工具自动生成代码技术。

用什么反射,效率低,代码也很难看
我宁愿重复
49 楼 ajoo 2007-03-01  
yyzheng 写道
呵呵 我觉得楼主在最后一贴的“疯狂”是有道理的,当整个系统中大部分业务处理逻辑都是“求和”的情况下,用一种方式将这种逻辑抽象出来我认为也不为过,只不过他用的技巧有点让人眼晕,不过没关系,因为一个团队里总会出一两个这样的“疯子”,让这些“疯子”维护这种代码即可,从架构的角度上看,楼主明显已经抽象出一层业务处理逻辑(hoops...难不成这就是传说中的业务平台。。。)
从design的方向上,我赞成楼主的观点,但从design的技巧上我不认同楼主的技巧,why:因为楼主利用了原有设计上的一个缺陷,就是对每一个class原来的设计都强迫其实现一个接口,这种取巧的方式不利于以后architecture的重构,而且楼主为了让没有实现接口的class也能用到重构后的模块,又新加了一些特定的接口,而这些接口是没有任何业务语义的,仅仅是为了用到重构后的模块而加的(hoops...难不成这就是传说中的侵入。。。)好吧,如果你想让按楼主重构后的架构clean一些的话(我承认我有一些技术洁癖),又需要再付出努力组织这些东东,而这样的代价是否值得呢,这实际上就是在重新架构你的system。。。


理解万岁。呵呵。

不过,这些新的接口是不要求被sum的类来"implements"的。因为“酒窝”可以搞定这一切。所以,不能算“侵入”吧?
48 楼 yyzheng 2007-02-28  
呵呵 我觉得楼主在最后一贴的“疯狂”是有道理的,当整个系统中大部分业务处理逻辑都是“求和”的情况下,用一种方式将这种逻辑抽象出来我认为也不为过,只不过他用的技巧有点让人眼晕,不过没关系,因为一个团队里总会出一两个这样的“疯子”,让这些“疯子”维护这种代码即可,从架构的角度上看,楼主明显已经抽象出一层业务处理逻辑(hoops...难不成这就是传说中的业务平台。。。)
从design的方向上,我赞成楼主的观点,但从design的技巧上我不认同楼主的技巧,why:因为楼主利用了原有设计上的一个缺陷,就是对每一个class原来的设计都强迫其实现一个接口,这种取巧的方式不利于以后architecture的重构,而且楼主为了让没有实现接口的class也能用到重构后的模块,又新加了一些特定的接口,而这些接口是没有任何业务语义的,仅仅是为了用到重构后的模块而加的(hoops...难不成这就是传说中的侵入。。。)好吧,如果你想让按楼主重构后的架构clean一些的话(我承认我有一些技术洁癖),又需要再付出努力组织这些东东,而这样的代价是否值得呢,这实际上就是在重新架构你的system。。。
47 楼 dreamstone 2007-02-27  
看了文章感觉很有意思,作者的思维挺好玩,感觉如果平时应用开发还是不要这样的好,如果是写一些特殊的需求,例如框架什么的还是不错的。另外再提供一个思路:
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("JavaScript");
for(int i=0;i<arr.length;i++){
    engine.put("obj", arr[i]);
    Object obj = engine.eval("obj."+method);
    if (obj instanceof Double){
        sum += ((Double)obj).doubleValue();
    }
}

是用jdk1.6中的script就可以简单实现了,好处是写起来方便,学习成本低。坏处是也存在重构问题,并且内部实现和动态代理、反射等也差不哪里去。
46 楼 yiding_he 2007-02-27  
思路新颖,快刀斩乱麻!
45 楼 qingjian 2007-02-27  
感觉 lz好痛苦

相关推荐

    Python 语言有什么奇技淫巧吗?.docx

    Python 语言奇技淫巧总结 Python 语言作为一种高效、灵活的编程语言,具有许多奇技淫巧,可以帮助开发者提高编程效率和代码质量。在本文中,我们将探讨 Python 语言中的四大奇技淫巧:循环代码优化、异常处理,以及...

    来自小密圈里的那些奇技淫巧.pdf

    标题中提到的“来自小密圈里的那些奇技淫巧”可能是指在特定的技术圈子中分享的一些特殊技巧,用于突破安全限制和进行有效的攻击测试。以下将详细解释文档中提及的各个技巧: 1. EVAL长度限制突破技巧: 在PHP中,`...

    Python那些事——python的奇技淫巧

    Python,作为一门广受欢迎的编程语言,在...总之,Python的这些“奇技淫巧”展示了这门语言在实际编程中灵活多变的应用能力,对于学习和使用Python的开发者而言,了解这些技巧能够极大地提高开发效率和程序的用户体验。

    算法心得-高效算法的奥秘(原书第2版)_带书签_高清_[位运算的奇技淫巧].pdf

    《算法心得-高效算法的奥秘(原书第2版)》是一本深入探讨算法精髓的书籍,...书中的"位运算的奇技淫巧"部分,无疑是将这种技术运用到极致的探索,对于有志于提升算法水平的开发者来说,是一份不可多得的参考资料。

    vue有哪些奇技淫巧.md

    vue有哪些奇技淫巧

    JavaScript奇技淫巧45招 _ 不可能不确定1

    JavaScript是世界上最流行的编程语言之一,尤其在Web开发领域中占据着核心地位。它不仅可以用于创建交互式的网页,还可以用于移动应用开发、服务器端编程...在JavaScript的世界里,还有很多其他的奇技淫巧等待探索。

    渗透中关于dns的奇技淫巧.pdf

    【DNS基础及安全应用】 DNS(Domain Name System)是互联网中的关键组件,负责将人类可读的域名转换为网络可以理解的IP地址。其工作原理是通过查询和响应机制,确保用户能正确访问网络资源。当用户输入一个域名时,...

    RocketMQ 奇技淫巧之 ServiceLoader 源码解读 - GitChat

    RocketMQ 奇技淫巧之 ServiceLoader 源码解读 抓下来打包成了HTML文件, 方便离线观看

    开源电子书《JavaScript奇技淫巧》,本书向您分享一系列新奇、有趣、有技术含量的 JavaScript 编程技术

    开源电子书《JavaScript奇技淫巧》,『奇技淫巧』一词出自古籍《书·泰誓下》,指新奇的技艺和作品。此词意与本书内容十分契合,因此书名借用该词。

    Go-Git的奇技淫巧

    在这个“Go-Git的奇技淫巧”中,我们将探讨一些高级和实用的Git技巧,以及如何使用Go来进一步操作Git。 1. **分支管理**:Git的分支机制允许开发者在同一项目上并行工作。`git branch`命令用于创建、查看和切换分支...

    Java 8中Stream API的这些奇技淫巧!你Get了吗?

    Java 8 中 Stream API 的奇技淫巧 Java 8 中的 Stream API 是一种功能强大且实用的数据处理工具,能够帮助开发者更高效地处理数据。Stream API 的出现是为了解决 Java 中处理数据的痛点,提供了一种简洁、灵活、...

    Git的奇技淫巧

    Git的奇技淫巧在软件开发领域中是一个广泛讨论的话题,因为它的功能强大且灵活性极高。Git不仅仅是一个“分布式版本管理工具”,它还是一个能够帮助开发者跟踪代码变化、协同工作和回溯错误的强大系统。在JavaScript...

    C语言中的奇技淫巧

    C语言中的奇技淫巧 C语言是一门功能强大且灵活的编程语言,学习C语言的过程中,总会遇到很多令人眼前一亮的代码,尤其是你写了几十行的代码,别人只用了简单几行的递归就实现的功能。下面我们就总结几个C语言中比较...

    CSS中边框使用负边距值的奇技淫巧

    标题“CSS中边框使用负边距值的奇技淫巧”暗示了负边距在CSS中的独特应用,这些应用可能并不常见,但能够解决特定的布局挑战。描述提到,通过负边距可以进行布局调整和实现元素重叠,这些都是设计师和开发者可以利用...

    移动端Ui自动化上的正经奇技淫巧Havefun

    在移动端UI自动化测试中,"正经奇技淫巧Havefun"这个标题暗示了我们将探讨一些巧妙而实用的技术,这些技术可能不广为人知,但能显著提高我们的工作效率。混合移动开发,特别是JavaScript开发,是现代移动应用开发的...

    Git的奇技淫巧.zip

    Git是一个 “分布式版本管理工具”,简单的理解版本管理工具:大家在写东西的时候都用过 “回撤” 这个 功能,但是回撤只能回撤几步,假如想要找回我三天之前的修改,光用 “回撤” 是找不回来的。...

    bash奇技淫巧

    ### bash奇技淫巧 #### 简介 在日常的Linux使用过程中,熟练掌握Bash的一些技巧可以极大地提升工作效率。本文将分享一系列实用的Bash小贴士,旨在帮助那些经常与Linux命令行打交道的人们更好地利用Bash的强大功能...

    Python-收集到的一些src挖掘奇技淫巧

    这个压缩包文件"Python-收集到的一些src挖掘奇技淫巧"可能是包含了一系列用于Python源代码安全检查和分析的技巧和工具。下面我们将深入探讨一些相关的知识点。 1. **源代码分析**:这是对软件项目源代码进行的详细...

Global site tag (gtag.js) - Google Analytics