`

通过行为参数化传递代码(Java)

阅读更多

行为参数化就是可以帮你处理频繁变更的需求的一种软件开发模式。

接下来,让我们来看看如何应对不断变化的需求。

 

1.初试牛刀:筛选绿苹果
一种常见的实现方式可能是这样:

public static List<Apple> filterGreenApples(List<Apple> inventory) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if ("green".equals(apple.getColor())) {
            result.add(apple);
        }
    }
    return result;
}

 

2.再展身手:把颜色作为参数
如果想要筛选红苹果,你该怎么做呢?简单的解决办法就是复制这个方法,把名字改成filterRedApples,然后更改if条件来匹配红苹果。
然而,如果想要筛选多种颜色:浅绿色、暗红色、黄色等,这种方法就应付不了了。

一个良好的原则是在编写类似的代码之后,尝试将其抽象化。

一种做法是给方法加一个参数,把颜色变成参数,这样就能灵活地适应变化了:

public static List<Apple> filterApplesByColor(List<Apple> inventory, String color) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (apple.getColor().equals(color)) {
            result.add(apple);
        }
    }
    return result;
}

 

3.新需求:按重量筛选苹果
一种可能的实现方式,是另写一个按重筛选苹果方法:

public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (apple.getWeight() > weight) {
            result.add(apple);
        }
    }
    return result;
}

优点
解决思路不错,不与筛选颜色的方法耦合,做到
一个方法只做一件事情,简单明了。

缺点
复制了大部分的代码来实现遍历库存,并对每个苹果应用筛选条件。
这有点儿令人失望,因为它
打破了DRY(Don't Repeat Yourself,不要重复自己)的软件工程原则

如果你想要改变筛选遍历方式来提升性能呢?那就得修改所有方法的实现,而不是只改一个。从工程工作量的角度来看,这代价太大了。

 

4.糟糕的实现方式:一个方法筛选多个属性
如果筛选的属性较多,由于前一种实现方式会产生大量的重复代码,于是另一种可能的实现方式如下:

public static List<Apple> filterApples(List<Apple> inventory, String color, int weight, boolean flag) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if ((flag && apple.getColor().equals(color))
                || (!flag && apple.getWeight() > weight)) {
            result.add(apple);
        }
    }
    return result;
}

通过一个方法来实现对多个属性的筛选,看似简化了代码,防止出现大量重复性代码。
但却存在以下缺点

  1. 代码复杂度明显增加,可阅读性也变差了。如果筛选的属性越多,这个缺点会越明显。

  2. 从使用者的角度来看,接口参数变得难以理解了。比如flag代表什么意思,什么时候传color或weight,这无形增加了使用者对接口理解的成本。

  3. 如果还有其他属性需要筛选,比如大小、形状等,那接口将会变得异常复杂。

  4. 对于这样复杂的一个接口,使用时可能还会传错参数

 

5.行为参数化:多种行为,一个参数
一种更高层次的方式,是将筛选的行为抽象化,将筛选的逻辑作为参数来传递。这样一方面可以复用遍历逻辑,另一方面可以应对多种多样的筛选需求

抽象出一个通用的筛选接口:
public interface ApplePredicate {
    boolean test(Apple apple);
}

按重量筛选:
public class AppleHeavyWeightPredicate implements ApplePredicate {
    public boolean test(Apple apple) {
        return apple.getWeight() > 150;
    }
}
按颜色筛选:
public class AppleGreenColorPredicate implements ApplePredicate {
    public boolean test(Apple apple) {
        return "green".equals(apple.getColor());
    }
}

利用ApplePredicate改过之后,filter方法看起来是这样的:

public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (p.test(apple)) {
            result.add(apple);
        }
    }
    return result;
}

这有点类似于“策略设计模式”,先定义一族算法,把它们封装起来(称为“策略”),然后在运行时选择一个实现策略。
在这里,算法族就是ApplePredicate,不同的策略就是AppleHeavyWeightPredicate和AppleGreenColorPredicate。

 

行为参数化的好处
可以
把迭代要筛选的集合的逻辑与对集合中每个元素应用的行为区分开来。这样你可以重复使用同一个方法,给它不同的行为来达到不同的目的。(将不变与变化部分进行分离

 

6.匿名类
通过行为参数化,我们似乎已经找到了较为理想的实现方式了。
但是,
行为参数化仍然存在一个问题,就是对于每个行为的传递,必须通过实现一个封装该行为的类,最后实例化后再进行传递。

可不可以再简单一点呢?对于Java 8之前,答案是匿名类
对于匿名类,我们或多或少都会在项目中使用,想必大家应该都不会陌生了。例如GUI事件处理、Comparator排序实现、Runnable执行代码块等。

GUI事件处理
Button button = new Button("Send");
button.setOnAction(new EventHandler<ActionEvent>() {
    public void handle(ActionEvent event) {
        label.setText("Sent!!");
    }
});

Comparator排序
inventory.sort(new Comparator<Apple>() {
    public int compare(Apple a1, Apple a2) {
        return a1.getWeight().compareTo(a2.getWeight());
    }
});

Runnable执行代码块
Thread t = new Thread(new Runnable() {
    public void run{
        System.out.println("Hello world");
    }
});

通过创建一个用匿名类实现ApplePredicate的对象,重写筛选的例子:

List<Apple> redApples = filterApples(inventory, new ApplePredicate() {
    public boolean test(Apple apple) {
        return "red".equals(apple.getColor());
    }
});

 

7.使用Lambda表达式
接下来,我们再来看看Java 8引入Lambda表达式的实现方式:

List<Apple> result = filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));

不错,干净漂亮。一方面更好的陈述了问题本身,另一方面解决了代码的啰嗦问题

 

8.持续改进:将List类型抽象化
通过
类型泛型化,使筛选逻辑更加通用。

public interface Predicate<T{
    boolean test(T t);
}

public static <T> List<T> filter(List<T> list, Predicate<T> p) {
    List<T> result = new ArrayList<>();
    for (T e : list) {
        if (p.test(e)) {
            result.add(e);
        }
    }
    return result;
}

List<Apple> redApples = filter(inventory, (Apple apple) -> "red".equals(apple.getColor()));
List<Integer> evenNumbers = filter(numbers, (Integer i) -> i % 2 == 0);

怎么样,既灵活又简洁,还不赶紧使用Java 8亲测一把^_^。

 

小结

  • 行为参数化(类、匿名类、Lambda):灵活,值参数化:死板。

  • 行为参数化可让代码更好地适应不断变化的要求,减轻未来的工作量。

  • 将不变与变化部分解耦,既复用了代码,又保证了灵活性。

  • 没有最好只有更好,持续改进代码。

以上内容整理自《Java 8 实战》第2章,排版及内容略做调整,更易于渐进理解。

 

转载请注明来源:http://zhanjia.iteye.com/blog/2426409

 

 

个人公众号

二进制之路

 

 

 

 

 

 

0
0
分享到:
评论

相关推荐

    Java行为参数化功能详解.pdf

    Java行为参数化是一种编程技术,它允许我们将行为(如条件检查)作为参数传递给方法,从而使代码更加灵活和可重用。在Java中,这通常通过接口和匿名内部类实现,尤其是在Java 8引入Lambda表达式之后变得更加简洁。...

    Java参数传递PPT

    Java参数传递的规则是:**Java只使用值传递,但这种值传递对于对象参数表现为类似引用传递的行为。** 在值传递中,函数或方法接收的是原始数据类型(如int、double、boolean)参数的副本。例如,如果有一个方法...

    java新手代码适合初学者简单经典.zip

    初学者会学习如何定义和调用方法,以及如何传递参数和返回值。 4. **类与对象** - **类(Class)**:Java是一种面向对象的语言,类是创建对象的蓝图。test1.java和test2.java可能包含至少一个或多个类定义,展示了...

    java基本程序代码

    了解如何定义、调用和参数传递是提高代码复用性的关键。 5. **数组**:Java中的数组用于存储同类型的数据集合,可以是一维、二维或多维。熟练使用数组能够处理大量数据。 6. **类与对象**:面向对象编程的核心是类...

    Java源代码

    Lambda表达式的核心概念是行为参数化,允许将代码视为数据进行传递。 2. **方法引用和构造器引用**:在Java 8中,可以使用方法引用或构造器引用直接传递方法或构造器作为参数,避免了Lambda表达式的冗余代码。 3. ...

    java初学者代码

    4. **函数和方法**:Java中的函数是可重用的代码块,可以接收参数并返回值。学习如何定义、调用和传递参数给方法是理解面向对象编程的基础。 5. **类与对象**:面向对象编程的核心在于类和对象。类是创建对象的模板...

    JUXING.zip_AppRetCode_参数化

    在编程领域,参数化是一种非常重要的技术,它允许我们通过传递不同的参数来改变代码的行为,从而使代码更加灵活和可重用。"JUXING.zip_AppRetCode_参数化"这个项目,显然关注的是如何在应用中实现参数化的矩形绘制...

    TestNg_0920。自己练习关于selenium+java。数据驱动模式、页面对象模式、行为驱动开发的代码

    【TestNg_0920】是一个练习项目,主要涵盖了Selenium与Java的结合使用,以及数据驱动模式、页面对象模式和行为驱动开发(BDD)等关键概念。以下是这些知识点的详细说明: 1. **Selenium**:Selenium 是一个广泛使用...

    Java学习资料 Java练习代码 Java基础语法,日常代码练习

    6. **异常处理**:Java通过try-catch-finally语句来捕获和处理运行时错误,确保程序的健壮性。 7. **集合框架**:List(如ArrayList, LinkedList)、Set(如HashSet, TreeSet)和Map(如HashMap, TreeMap)是Java...

    接口编写java代码

    在本案例中,虽然没有明确地使用Java接口关键字`interface`来定义一个接口类型,但通过提供的代码片段可以看出,该代码片段主要实现了某种特定功能——即获取安全天数,并且这个功能是通过一个具体的类及其方法实现...

    java大学使用教程部分例题源代码

    - 参数传递:学习如何通过参数向方法传递数据,并在方法内部处理。 3. **类与对象** - 类的定义:源码会展示如何定义类,包括属性(字段)和行为(方法)。 - 对象的创建与使用:如何通过new关键字实例化对象,...

    java入门实例代码

    学习如何定义、调用和传递参数给方法是重要的一步。 6. **字符串处理**:Java中的`String`类提供了丰富的操作字符串的方法,如拼接、查找、替换等。 7. **异常处理**:通过try-catch-finally语句块,Java允许...

    java初学者学习源代码

    学习如何定义、调用方法以及参数传递是提升代码复用性的关键。 6. **数组与集合**:数组用于存储同类型的数据,集合(如ArrayList, LinkedList, HashSet等)则提供了更灵活的数据组织方式。学习如何创建、遍历和...

    《Java十大经典案例》源代码

    通过案例,你可以学习如何定义、调用和重载方法,理解参数传递机制,以及理解静态与非静态方法的区别。 4. 类与对象:类是Java中定义对象的蓝图,而对象是类的实例。案例会教你如何定义类,声明成员变量和方法,...

    Java核心技术(原书第九版)源代码

    5. **函数与方法**:Java的方法是实现功能的独立代码块,可以通过参数传递数据并返回结果。源码将展示如何定义、调用和重载方法。 6. **异常处理**:Java的异常处理机制是通过try-catch-finally块来捕获和处理运行...

    java经典实例 代码简短详细

    - **多态性**: 在 `Feeder` 类中,通过将 `Animal` 和 `Food` 类型作为参数传递给 `feed` 方法,实现了多态性的应用。这意味着可以通过同一个方法来处理不同的动物进食行为。 #### 3. 抽象类与继承 - **抽象类**: `...

    Jsp代码java项目例子

    【Jsp代码java项目例子】是一个集合了21个实用示例的Java Web项目,旨在帮助开发者更好地理解和应用JavaServer Pages(JSP)技术。这些小例子覆盖了JSP的基础到高级特性,对于初学者和有经验的开发人员来说都是宝贵...

    JAVA核心技术(代码)

    理解参数传递和返回值的概念是学习方法的重点。 5. **异常处理**:Java提供了异常处理机制,通过try-catch-finally语句块来捕获和处理运行时错误。 6. **字符串操作**:Java中的String类提供了丰富的操作字符串的...

    设计模式Java版各个实现代码

    9. **命令模式(Command Pattern)**:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。Java中的命令模式常用于GUI编程中。 10. **责任链...

    java及C++中传值传递、引用传递和指针方式的理解

    Java和C++对待参数传递有着不同的处理机制,这主要体现在值传递、引用传递和指针方式上。 首先,我们来看Java的值传递。Java中,所有的参数传递都是基于值的。这意味着当一个对象作为参数传递给方法时,实际上是...

Global site tag (gtag.js) - Google Analytics