java - Java 8 是否提供了访问者模式的替代方案?

标签 java oop functional-programming java-8

This关于函数式编程和面向对象编程之间的区别,Stack Overflow 上的流行答案是这样说的:

Object-oriented languages are good when you have a fixed set of operations on things, and as your code evolves, you primarily add new things. This can be accomplished by adding new classes which implement existing methods, and the existing classes are left alone.

Functional languages are good when you have a fixed set of things, and as your code evolves, you primarily add new operations on existing things. This can be accomplished by adding new functions which compute with existing data types, and the existing functions are left alone.

假设我有一个 Animal 界面:

public interface Animal {
    public void speak();
}

我有一只DogCatFishBird,它们都实现了该接口(interface)。如果我想向 Animal 添加一个名为 jump() 的新方法,我将不得不遍历所有子类并实现 jump().

访问者模式可以缓解这个问题,但似乎随着 Java 8 中引入的新功能特性,我们应该能够以不同的方式解决这个问题。在 scala 中,我可以轻松地使用模式匹配,但 Java 还没有。

Java 8 真的让在现有事物上添加新的操作变得更容易了吗?

最佳答案

您要完成的工作虽然令人钦佩,但在大多数情况下并不适合 Java。但在我开始之前......

Java 8 为接口(interface)添加了默认方法!您可以根据接口(interface)中的其他方法定义默认方法。这已经可用于抽象类。

public interface Animal {
    public void speak();
    public default void jump() {
        speak();
        System.out.println("...but higher!");
    }
}

但最终,您将不得不为每种类型提供功能。我没有看到添加新方法和创建访问者类或部分函数之间的巨大差异。只是地点的问题。你想按 Action 或对象组织你的代码吗? (功能性或面向对象,动词或名词等)

我想我想表达的意思是 Java 代码是按“名词”组织的,原因不会很快改变。

访问者模式和静态方法可能是您按操作组织事物的最佳选择。但是,我认为访问者在不真正依赖于所访问对象的确切类型时最有意义。例如,可以使用 Animal visitor 让动物说话然后跳跃,因为所有动物都支持这两件事。跳跃访客对我来说意义不大,因为这种行为对于每只动物来说都是固有的。

Java 使真正的“动词”方法有点困难,因为它根据参数的编译时类型选择要运行的重载方法(参见下文和 Overloaded method selection based on the parameter's real type)。方法仅根据 this 的类型动态分派(dispatch)。这就是继承是处理此类情况的首选方法的原因之一。

public class AnimalActions {
    public static void jump(Animal a) {
        a.speak();
        System.out.println("...but higher!");
    }
    public static void jump(Bird b) { ... }
    public static void jump(Cat c) { ... }
    // ...
}
// ...
Animal a = new Cat();
AnimalActions.jump(a); // this will call AnimalActions.jump(Animal)
                       // because the type of `a` is just Animal at
                       // compile time.

您可以使用 instanceof 和其他形式的反射来解决这个问题。

public class AnimalActions {
    public static void jump(Animal a) {
        if (a instanceof Bird) {
            Bird b = (Bird)a;
            // ...
        } else if (a instanceof Cat) {
            Cat c = (Cat)a;
            // ...
        }
        // ...
    }
}

但现在您只是在做 JVM 为您设计的工作。

Animal a = new Cat();
a.jump(); // jumps as a cat should

Java 有一些工具可以更轻松地向大量类添加方法。即抽象类和默认接口(interface)方法。 Java 专注于基于调用方法的对象来分派(dispatch)方法。如果您想编写灵活且高性能的 Java,我认为这是您必须采用的一种习惯用法。

P.S. 因为我是 That Guy™,所以我将介绍 Lisp,特别是 Common Lisp Object System (CLOS)。它提供了基于所有参数进行分派(dispatch)的多重方法。 Practical Common Lisp 一书甚至提供了 an example of how this differs from Java .

关于java - Java 8 是否提供了访问者模式的替代方案?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29090179/

相关文章:

java - 如何比较基本类型中忽略大小写的字符

java - 通过缩放级别更改半径

java - 如何自动更改 WebStart 程序的 Java 缓存文件夹

java - 是否存在推断协方差

r - S4:使用类属性作为类方法的默认输入值

java - 了解 JavaPairRDD.reduceByKey 函数

Scala:如何理解Try的flatMap方法?

java - 与 ThreadLocal 同步还是 InheritableThreadLocal?

sql - SOLID 存储过程和函数

performance - Applicative functor vs monad 在 Scala 中的组合性能