`

java类中 多个方法求和.

阅读更多

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

我们有一个简单的java类:

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



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

Java代码 复制代码
  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. }  
static double getOverallBalance(Details[] arr){
  double sum = 0;
  for(int i=0; i<arr.length; i++) {
    sum += arr[i].getBalance();
  }
}



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

Java代码 复制代码
  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. }  
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接口,如下:

Java代码 复制代码
  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. }  
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]);
  }
}


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

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

Java代码 复制代码
  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. ....  
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(),最终的语法会是:

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


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

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

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

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

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



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

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



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

Java代码 复制代码
  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. }  
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的语法可以被简化为如下:

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



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


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

要是在ruby里,我就直接:

Java代码 复制代码
  1. sum(arr){balance}  
sum(arr){balance}



该死的java啊!

分享到:
评论

相关推荐

    java 入门程序两个数求和

    在Java中,你可以通过定义方法来实现这个功能。例如,你可以创建一个名为`addNumbers(int num1, int num2)`的方法,该方法接受两个整数参数,并返回它们的和。这个方法的实现如下: ```java public int addNumbers...

    java数据求和

    这时,可以考虑使用`java.util.stream.Collectors.sumInt()`函数对流中的整数进行求和,这种方法可以有效地处理大量数据,避免一次性加载所有数据到内存。 总的来说,Java实现文件数据求和涉及文件读取、数据解析、...

    java 数组求和计算

    - **数组操作函数**:Java的`Arrays`类提供了许多实用方法,如`sort()`排序数组,`equals()`比较数组内容,`copyOf()`复制数组等。 - **集合框架**:`ArrayList`和`LinkedList`等集合类可以动态增长,提供了更多的...

    java8 利用reduce实现将列表中的多个元素的属性求和并返回操作

    在 Java8 中,我们还可以使用 Stream 的其他方法来实现列表元素的属性求和操作,例如使用 collect 方法来将列表中的元素收集到一个新的集合中,然后使用 Stream 的 sum 方法来求和。 例如,我们可以使用以下代码来...

    java-leetcode题解之第67题二进制求和.zip

    在这个压缩包文件中,我们可能找到一个或多个Java源代码文件,这些文件包含了对第67题的解答。描述中的“java_leetcode题解之第67题二进制求和”进一步确认了这个主题,意味着我们可能会看到如何用Java语言实现将两...

    java8 stream自定义分组求和并排序的实现

    首先,我们需要定义一个实体类 `GroupDetailDTO`,该类具有多个属性,包括 `headsetId`、`time`、`actConcreteTime` 和 `played`。然后,我们可以使用 Java8 Stream 的 `collect` 方法对集合进行分组求和,并使用 `...

    2023年Java面向对象程序设计实验报告.doc

    2. 设计思路:定义两个类 Person 和 TestPerson,Person 类满足上述规定,TestPerson 类包含一个方法 public static void sortPerson(Person[] personList),在该函数中运用 Person 类的 older 方法实现对数组 ...

    30个java工具类

    Java工具类是编程实践中不可或缺的一部分,它们提供了许多通用的功能,...以上就是Java工具类可能涉及的一些核心知识点,涵盖了Java开发中的多个方面。掌握这些工具类和相关概念,能够有效提升Java开发的效率和质量。

    java1-6章测试题目.docx

    方法的重载(Overloading)是指在同一个类中定义多个同名方法,但参数列表不同。这有助于提高代码的可读性和复用性。 `return`不一定只在方法末尾使用,可以在满足某种条件时提前返回。 `switch`语句的表达式可以...

    Java万能进制转换器.doc.doc

    - `MyWindowListener` 类:这个类扩展了 `java.awt.event.WindowAdapter`,用于监听窗口事件。它覆盖了 `windowClosing` 方法,当用户尝试关闭窗口时,会调用 `System.exit(0)` 来结束程序运行。 - `ScaleFrame` ...

    day002 JAVA语法基础-方法.doc

    - **示例**:可变参数可以用于简化处理多个相同类型参数的方法,例如求和方法,可以接受任意数量的整数。 4. **Arrays工具类及API的使用**: - **Arrays类**:Java提供了一个内置的`java.util.Arrays`工具类,...

    Java开发零基础篇:day05 方法.pdf

    - 类中可以定义多个方法,方法和方法是平行的,不能在方法内部定义另一个方法。 - 方法中可以使用return关键字,它可以用来返回一个值给方法的调用者,也可以用来提前退出当前方法。 通过上述知识点的学习,零...

    Java中的方法重载(overload).docx

    Java中的方法重载(Overload)是面向对象编程的一个重要特性,它允许我们在同一个类中定义多个具有相同名称但参数列表不同的方法。方法重载的关键在于“多态性”(Polymorphism),即同名方法可以通过不同的参数形式...

    java_java试题及答案.pdf

    这份"java_java试题及答案.pdf"包含了多个关于Java技术的知识点,涵盖了基础语法、类与对象、异常处理、输入输出、图形用户界面(GUI)以及继承等多个方面。 1. **Java应用程序与Applet**: - Java Application的...

    java8实现list集合中按照某一个值相加求和,平均值等操作代码

    如果我们有一个User对象的List,并想计算所有User的年龄总和,我们可以使用Java 8的流API中的`summingInt()`方法: ```java List&lt;User&gt; users = ... // 初始化用户列表 int totalAge = users.stream() .collect...

    java经典小程序(1).pdf

    1. Scanner类的应用:文件中多次出现import java.util.Scanner,表明程序中使用了Scanner类来从标准输入获取用户输入。Scanner类是Java的一个工具类,属于java.util包,提供了多种读取不同数据类型的方式。 2. 流程...

    Java8新特性示例代码

    Lambda表达式主要用于实现接口中的抽象方法,尤其是那些只有一个抽象方法的接口,这类接口被称为函数式接口,例如`java.util.function.Consumer`、`java.util.function.Function`等。 ### 使用Lambda表达式简化代码...

    jiafa.rar_求和

    这个简单的求和程序虽然基础,但它涵盖了软件开发中的多个核心概念,包括编程语言、用户界面设计、事件处理、数据运算以及异常管理等。通过深入理解这些知识点,开发者可以构建更复杂、功能丰富的应用。

    基于JAVA的象棋游戏设计与实现.pdf

    它不仅涉及到软件开发的多个方面,包括面向对象设计、网络编程、GUI设计,还强调了性能需求在游戏开发中的重要性。通过这些知识点的分析与应用,开发人员能够设计出稳定、高效和用户友好的网络对弈平台。文档中的...

Global site tag (gtag.js) - Google Analytics