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

我们有一个简单的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啊!
分享到:
评论
44 楼 jindw 2007-02-27  
预加之罪,何患无词
43 楼 zwhua 2007-02-25  
真有意思!呵呵
42 楼 intolong 2007-02-25  
呵呵,一看开头就是酒窝
41 楼 cavvie 2007-01-03  
技巧很好,但是有点过了,在团队开发中这种方式应是不受欢迎的。

我想不是为了编程而编程,也不是为了展示技巧而编程,以清晰、高效的方式实现编写出强壮的代码,满足需求是最重要的。

需要自己去用心和关注的事情很多,完全没有必要浪费时间于这种让自己自觉是高手的技巧中。
40 楼 差沙 2007-01-02  
ruby没了拐棍到是真的。鱼与熊掌很多情况下不能兼得的。
39 楼 歆渊 2007-01-01  
ray_linn 写道
貌似歇斯底里了,哈哈。

所谓的大牛的通病是容不得别人戳指头,于是愤怒,郁闷,极度愤怒,极度郁闷。


如果真是这样, 那也应该算大牛可以作出超出普通人成绩的一个本质原因了
38 楼 bigshell 2006-12-31  
看了半天还是觉得这句话最为中肯:

淫一手好湿不难,难的是淫一被子好湿
37 楼 ray_linn 2006-12-30  
貌似歇斯底里了,哈哈。

所谓的大牛的通病是容不得别人戳指头,于是愤怒,郁闷,极度愤怒,极度郁闷。
36 楼 ajoo 2006-12-30  
淫一手好湿不难,难的是淫一被子好湿。继续。

怀着内疚的心情,虽然我自己也对这个sumOf()感觉很矛盾,虽然很多朋友对这个东西恨得牙痒痒,世上不如意的事情常八九,这个东西,居然就进入了production code里了。毕竟,如果极端实用主义的话,从客户代码角度看,它用起来是最爽的。

就在我快要把自己做了这么一件亏心事的事实忘掉的时候,我居然发现有一种忍不住再次犯罪的倾向。

事情的发生是这样的。

这个遗留系统是一个金融软件。不知道是不是世界上搞金融的都是头脑简单的西装男的原因,这个系统里面居然几乎到处都有对一个数组求和的操作。前面我发现的对一个object model重复七次的求和居然仅仅是冰山的一角。在陆续地对系统熟悉的过程中,发现"for(...){sum+=arr[i]}"这种操作真是到处都是。辛勤的程序员们从来都是遇到一个求和就写一遍for loop,从来没有听他们抱怨过,多好的同志啊!

于是,我就想,恩,根据所谓窃钩者诛,窃国者王的普遍真理,既然我已经搞了奇迹淫巧了,一不做二不休,干脆就干到底了。这个dynamic proxy从实现看并不是只能局限于Account这一个object model的。为什么不能把所有的四行的求和操作都换成一行的:
double totalWhatever = sumOf(WhateverInterface.class, whateverArray).getWhateverValue();


然后这个dynamic proxy就可以从一个尴尬的偷娶回来的二奶的地位一跃成为圣上钦赐的二太太甚至大妇也说不定阿。然后它就变成了一个需要学习的"pattern",而不是一个需要审视的奇迹淫巧了。

恩。就这么办。

改动dyn proxy不难,(其实,几乎没什么需要改的)。就是有一个毛病啊一个毛病:它要求被求和的object model必须实现一个接口,不管什么接口,但是必须有个接口(否则dynamic proxy不好使阿)。


幸运的是,这个遗留系统一定最初是被一个刚刚接触“program against interface”理论而把这个理论奉如神明的好同志设计的,几乎所有的东西都有接口。这么说吧,就算你要的仅仅是一个Pair,一个只有getFirst()和getSecond()的java bean,它也会有一个IPair的接口?为啥?不为啥,就为了“什么东西都应该有个接口”的金科玉律。

当初我没少破口大骂这个棒槌,真是成事不足败事有余。该用接口的地方偏偏用的是继承,而明明多此一举的地方就非要碍眼地站着一个ISomething。接口本来应该是用来驳接系统的各个模块,隔离实现细节用的,他却是内部该耦合的还是耦合,仅仅在每个东西的外面包上一层金光闪闪的接口来证明“今天我接口了”。靠!

现在,第一次发现这家伙原来也有可爱的一面。(声明,我仍然反对滥用接口,某个人随便乱扔瓜果皮核结果凑巧让一个追杀我的坏蛋摔了一交,固然让人高兴,但是不代表他不应当被罚款)



不过,虽然说大部分的情况如此,还是有少部分东西没有接口的。咋办?自己写的类,还可以死契白列地加上这个接口。那要是一个jar里面的类呢?要是axis自动生成的呢?

说到这,想起了几天前讨论的这个框架侵入性问题了:http://www.iteye.com/topics/next?topic=39330&forum=39

要说我要求每个类都实现一个接口,这不是侵入么?而且是赤果果的侵入把?

怎么办尼?哈,继续炫耀,使用酒窝:http://ajoo.iteye.com/blog/38299,把dynamic proxy的代码变成如下模样:
public class All implements InvocationHandler, Serializable {
  private static final long serialVersionUID = 3992411460899603473L;
  private final List participants;
  private final Class targetType;
  private final Implementor implementor;
  public static <T> T sumOf(Class<T> targetType, Object[] arr) {
    return sumOf(targetType, Arrays.asList(arr), arr.getClass().getComponentType());
  }
  @SuppressWarnings("unchecked")
  public static <T> T sumOf(Class<T> targetType, List<?> participants, Class<?> componentType) {
    return (T)Proxy.newProxyInstance(targetType.getClassLoader(), 
        new Class<?>[]{targetType}, new All(participants, targetType, 
            determineImplementor(targetType, componentType))
    );
  }
  private static Implementor<?> determineImplementor(
      Class<?> targetType, Class<?> componentType) {
    return componentType==null||targetType.isAssignableFrom(componentType)?
        null:Implementor.instance(componentType);
  }
  public static <T> T sumOf(Class<T> targetType, List<?> participants) {
    return sumOf(targetType, participants, null);
  }
  private Implementor getImplementorFor(Object obj) {
    if(implementor==null){
      return Implementor.instance(obj.getClass());
    }
    else return implementor;
  }
  @SuppressWarnings("unchecked")
  private Object convert(Object from){
    return getImplementorFor(from).implement(targetType, from);
  }
  private All(List participants, Class targetType, Implementor implementor) {
    this.participants = participants;
    this.targetType = targetType;
    this.implementor = implementor;
  }
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if(Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      }
      else {
        double result = 0;
        for(Iterator<?> it = participants.iterator(); it.hasNext();){
          Object participant = it.next();
          if(participant==null) continue;
          if(!targetType.isInstance(participant)) {
            participant = convert(participant);
          }
          Number val = (Number)method.invoke(participant, args);
          if(val != null)
            result += val.doubleValue();
        }
        return new Double(result);
      }
    }
    catch(InvocationTargetException e) {
      throw e.getTargetException();
    }
  }
  /**
   * if the object compared to is a dynamic proxy, unwrap it and get the InvocationHandler to compare with.
   */
  @Override
  public boolean equals(Object obj) {
    if(obj!=null && Proxy.isProxyClass(obj.getClass())){
      return super.equals(Proxy.getInvocationHandler(obj));
    }
    else return super.equals(obj);
  }
}

这个实现比最初的实现考虑的多了一些,因为要做大妇嘛,当然要考虑全面一些,于是,增加了对equals的支持,对generics的支持,支持了Serializable,同时支持了对数组和list的sum动作。
更重要的是,它用dimple来支持当被sum的类不实现目标接口的时候自动进行委托(所谓duck typing咯)

因此,当调用:
sumOf(TargetInterface.class, array).getBalance()

而array里面的元素没有实现TargetInterface的时候,代码仍然工作(只要你有getBalance()函数定义就成)


这样,当客户代码要对某一个没有接口的类求和的时候,可以额外声明一个interface,把要求和的方法写进去,不需要写任何实现,直接用就是了。

比如说:
public class MyAccount {//第三方类,没有实现任何接口
  public double getBalance(){...}
}
private interface IAccount {//我们自己的接口
  public double getBalance();
}
MyAccount[] accts = ...;
double totalBalance = All.sumOf(IAccount.class, accts).getBalance();



到这里,我又想到了在酒窝的那个帖子里和taowen的讨论。taowen希望酒窝能自动检查用来动态实现接口的类里面的每个方法,如果在接口里面没有匹配,就证明可能是笔误,直接报错。我当时就很犹豫,因为这似乎有些影响灵活性(虽然对stub test的目的来说这个灵活性似乎没什么用)。所以,我只给出了两个额外的类型检查函数和一个额外的annotation来让用户有选择地检查。

现在从这个例子里面可以看出这个灵活性的作用了。如果我做这个检查,那么就要求MyAccount里面所有的public方法都要在IAccount里面,明显不实用了。

好了,太多的炫耀了。一个简单的求和操作已经被我差不多引申到世界和平了。呵呵,原来我还遮遮掩掩地淫,这不,既然被撞破了,那就只好赤果果地淫了。

对了,无可争议地,本系列属于少儿不宜。需要不通过“由拙而巧,再返璞归真”的普通人方法,而是直接跳级学习“我拙,我拙,我大巧若拙”的无上大道的朋友们,不要被本系列误导了,你走错门了,这是男厕所,女厕所在隔壁。
35 楼 ajoo 2006-12-30  
继续我们的奇技淫巧之旅。


上面我们说了几种做sum的方法(怎么感觉象是回字的几种写法?)

1。质朴无华的"for(...){sum+=arr[i];}"。这个方法直观地象白开水,直来直去,没有一点花巧。这让我们这些很聪明的(至少我们喜欢这样认为)程序员很不爽。你能想象高傲地象凤凰一样的慕容复一遍一遍地黑虎掏心么?而且,就算我们要面对讨厌的极端实用主义者的诘问,至少我们可以把几百分贝的“DO NOT REPEAT YOURSELF”和几十克拉的口水同时摔在他脸上。要求理论上应该很傻的业务逻辑程序员去写无数个四行的for loop,似乎也不太符合实用主义者的理想的。(如果“理想”这个词还能用在这些把灵魂出卖给了魔鬼的人身上的话)



2。用Getter接口来抽象。这是最中规中矩的面向对象方法论:遇到重复代码怎么办?把重复的地方提取出来,不同的地方用接口来抽象。如果说直接的"for loop"是黑虎掏心,这个方法就是少林七十二绝技。绝对的静态类型安全,绝对地优雅。整整十年,我们用这个方法几乎可以说打遍大江南北。所有的什么23个模式,说穿了都不过是这个思想方法在面对对手的不同招数时的各种衍化罢了。
唯一让人尴尬的,是我们这次面对的不是艰险狡诈的魔头,也不是助纣为虐的朝廷鹰爪。她,她就是对面那条街的面馆老板娘,今天慕容复碰巧吃面忘了带钱,这泼辣的女人头发长见识短,居然上来就挠。怎么办?来一招拈花指?面露微笑一下?对方看不懂,说俺是淫贼怎么办?(我哩!到底谁非礼谁呀?),运气不好再招来城管,被押去翻沙子?—— 跑题了 —— 抽象了半天,抽取了共性,我们每一步动作都绝对专业,结果最后发现“代码行数不减反多”。用黑皮的话说“你费那事干吗?”。



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

类似地,也可以自己直接用reflection,最终代码大同小异:
double totalRoi = sumOf(accountArray, "getReturnOfInvestment");

同样地,具体实现我就不写了,要是想象不出怎么做的话,你的定位看来让你不适合看这个“奇迹淫巧”,还是回去做山贼这份有前途的职业吧。

这个方案看起来已经相当好了。一行代码搞定,逻辑上几乎没有废话。拿ruby写,也就到这样了吧?不过就是
total_balance = sum_of(accountArray, :balance)

的区别而已。

用反射的方法呢,自己要做的工作稍微多一些,用起来也要多写个"get"。好处是灵活性稍有增加,对一些没有按照javabean规范命名的数据点也可以求和了(比如,求一串List的size()合)。不过,总的来说,半斤八两。两个方法都无法处理数据点存在参数的情况,比如:
for(int i=0; i<arr.length; i++){
  sum += arr[i].getReturnOfInvestment(true);//你别管这个true代表什么
}

相比之下,黑虎掏心和少林拳都可以轻松处理这个情况。

呵呵,其实呢,我也就是鸡蛋里挑骨头。目前还没有发现有需要参数的情况。何况,就算需要参数,你直接自己单独写一个for loop能死啊?

更致命的问题来自java和ruby这类动态语言的根本哲学差异:java是静态强类型的。这么多年了,我们早就习惯于依赖编译器告诉我们“不是getBalace(),你写错啦,是getBalance(),笨蛋!”。一旦某一天这个拐棍被撤去了,我们就像考试的时候本来约好的大牛同学被别人强行占有了一样地无助,忧郁和悲愤了。
我知道,gigix肯定会说:“你不写单元测试的么?” 好吧,就算我可以接受用考试后的红色的零蛋来鞭策自己的结果(毕竟,我们上大学的最终目的不是某次测验,而是找到好工作,对么?),那么重构呢?我还敢在eclipse里面随便rename method么?eclipse当然是有这个“同时替换字符串”的选项,不过这种东西就不是那么保险了。至少的,你不敢不经过review就直接强行替换吧?还有,上下文提示呢?自动完成功能呢?returnOfInvestment这个词可是有点臭长啊。

经过哲学的思辨,缜密的思考,严谨的求证,精密的实现,我们终于骄傲地宣布了这个有史以来最实用的summer库(夏天?)了。我们可以和蔼可亲平易近人亲切地告诉围绕在我们周围的感动的热泪盈眶的业务程序员:从今天开始,你们不用再自己写可怕的for loop了,(掌声)。漫漫长夜去过去了,今天我们终于迎来了第一缕曙光。你们唯一需要做的,就是如此如此,这般这般(掌声,经久不绝的掌声)。—— 当然,你们不能用上下文提示和自动完成了 —— 自然,这无足轻重,一个真正的程序员是不应该留恋这些无聊的东西的。——— 对了,顺便说一句,重构的时候稍微小心一些也就是了。

当然,我这是理想情况。现实是,不论演讲的是领导,还是随便哪个写这个库的精英专家程序员架构师,都不会这么傻实诚地自爆弱点的。演讲到了“经久不绝的掌声”,就已经结束了。后面的得靠使用者自己用血一般的教训来补充了。


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

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

当然,不管什么东西,都是有代价的,而我们当然从来是只报喜不报忧的。
这个方法最大的问题就在于:它太过魔术化了一点。用起来虽然很爽,可是一旦某个业务程序员自己觉得自己已经是个很优秀的程序员了,不安心本职工作,居然想刨根究底,就会发现这东西在光鲜的外表下隐藏着太多的机关。等你通过“学习”java 1.3的dynamic proxy机制,终于弄懂了sumOf()到底干了些什么,十有八九会破口大骂“这都是什么乱七八糟的?,就为了一个求和么?真想杀了那个写代码的自以为聪明的傻瓜。他难道没有听说过KISS原则么?”
而可悲的是,这个指责合情合理。为了一点点语法上的“自慰”般的快感,真的值得在内部搞这么多玄虚么?要知道,软件是一个需要维护的产品,只为了做一个求和这种简单地不能再简单的操作,就要付出团队内大部分人都看不懂代码的代价?(我承认,我认罪,我虚荣,我炫耀技巧——如果这也可以炫耀的话,不过要说“玄虚”,要说为了语法的快感而无恶不作,真希望指责的人研究研究modern c++ design,看看人家Alexandrei小侠那叫怎么个玩弄技巧,怎么个“玄虚”,跟人家相比,我真不好意思自己这点过家家的东西居然也敢称为“玄虚”,在这网络时代,难道是个人都敢玩玄虚了么?)

到底应该keep it simple and stupid还是do not repeat yourself?几行重复的for loop到底算不算一个需要克服的“重复”?一个dynamic proxy到底算不算“复杂”?是善还是恶?是生存还是死亡?你觉得你正在读的是屈原还是哈姆雷特?是梵高还是孔乙己?还是啥也不是,就一装腔作势,无病呻吟的死猪?





(待续)








34 楼 歆渊 2006-12-29  
我觉得这里还有一个隐藏的问题, 就是并发同步, 当然如果程序保证是单线程执行没有这个顾虑, 但是用在并发应用上, 问题可能很严重. 特别是以后内存越来越大了, CPU核心越来越多了, 应用架构也会更多偏向并发模式的.

动态语言可以容易实现很漂亮的语法, 但是却埋藏了并发互斥的问题和简单解决办法. 如果回过头来再看看最开始的 for 循环, 一旦这段程序要从单线程改为并发执行, 只需找合适的地方加个互斥资源对象, 和一些 synchronized(){} 块就可以了, 虽然解决起来比较笨拙, 但是容易发现和想到.

其实ajoo碰到的这个情况换作我的话也会很郁闷, 重复性的东西很容易让人乏味, 是最适合让机器去做的事情. 这样的问题还是在大前提, 整体上下文下, 从自然世界的本源问题出发, 逐环解决比较好. 本可以避免很多因为编程工具而引发的问题, 但从中间环节再开始想办法的话, 之前的很多问题就在所难免, 也就被无缘无故的拖累. 不过商业环境没有等待完美的耐性, 有时候郁闷也得去做, 没有办法.
33 楼 Godlikeme 2006-12-28  
ajoo喜欢研究问题,是深入的研究,很是佩服。只是提醒看的人,还是多花时间研究些基础问题,这种问题有空学习学习思想就行了。
32 楼 ray_linn 2006-12-28  
jianfeng008 写道
又没说哪里这样用了,这样秀出来大家讨论讨论不是很好嘛,不懂的人也有机会学学懂,永远接触简单的东西,哪来深入理解呢?
而且这里面的知识点不都是很有用嘛,我总结几点:
1.java的反射
2.代理模式
3.apache的beanutils工具包
4.javabean数据的另外几种存储方式(二纬数组(容器也可以),map),
5.用以上知识点思考,
总结得不一定正确,个人观点,我感觉对4能深入理解得人不见得多吧,很多web框架这些知识点都是基础,不理解的人用得好也是比较困难的吧(至少学习的曲线会复杂点,到最后会用了也许都没理解到呢)。



举这样的例子根本就是教坏小孩...而且搞得复杂西西的, 还突然出现了ruby, my god.
31 楼 jianfeng008cn 2006-12-27  
ray_linn 写道
说实话 Dy那个什么proxy十分的丑陋,是我见过的最丑的东西之一. 而且proxy这个词又十分含糊,鬼知道这里突然出现这个proxy,是什么原因什么由来,是不是天外又飞仙了?

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

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

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


lz又没说哪里这样用了,这样秀出来大家讨论讨论不是很好嘛,不懂的人也有机会学学懂,永远接触简单的东西,哪来深入理解呢?
而且这里面的知识点不都是很有用嘛,我总结几点:
1.java的反射
2.代理模式
3.apache的beanutils工具包
4.javabean数据的另外几种存储方式(二纬数组(容器也可以),map),
5.用以上知识点思考,
总结得不一定正确,个人观点,我感觉对4能深入理解得人不见得多吧,很多web框架这些知识点都是基础,不理解的人用得好也是比较困难的吧(至少学习的曲线会复杂点,到最后会用了也许都没理解到呢)。

30 楼 ray_linn 2006-12-26  
yelongyelong 写道
不是每个人都能卖弄滴吧



卖弄是最简单地,翻翻google就能搞定,倒是把程序写得平实大路,重剑无锋是最难的。

前者叫新潮,后者叫功底。
29 楼 yelongyelong 2006-12-25  
不是每个人都能卖弄滴吧
28 楼 ray_linn 2006-12-25  
说实话 Dy那个什么proxy十分的丑陋,是我见过的最丑的东西之一. 而且proxy这个词又十分含糊,鬼知道这里突然出现这个proxy,是什么原因什么由来,是不是天外又飞仙了?

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

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

那种感觉,真的就象柳永的词,卖弄的成分多,实用的成分少.
27 楼 bigshell 2006-12-25  
不错,学习了~
26 楼 云上的雨 2006-12-23  
我比较同意三楼的观点

我的疑惑就是这些技巧所带来的好处和这些技巧对于入门难度的影响

哪个更大一些
25 楼 梦秋雨 2006-12-22  
曾经有一个书法老师说,笔画越少的字,越难写好。

程序大概也是如此。越是简单的逻辑,想要找出最优的方案也就更难。当人了我是说对于某一个人来说。

相关推荐

    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文件, 方便离线观看

    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开发,是现代移动应用开发的...

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

    开源电子书《JavaScript奇技淫巧》是一本深入探讨JavaScript编程技术的教程书籍。书中涉及一系列富有创新性和技术深度的编程技巧,它不仅满足了开发者对于新技术的渴求,还提供了实际问题的解决方案。本书的标题借鉴...

    Git的奇技淫巧.zip

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

    bash奇技淫巧

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

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

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

Global site tag (gtag.js) - Google Analytics