Java 8是否提供了访问者模式的替代方案? [英] Does Java 8 provide an alternative to the visitor pattern?

查看:147
本文介绍了Java 8是否提供了访问者模式的替代方案?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Stack Overflow这个受欢迎的答案有这个说功能性编程和面向对象编程之间的区别:


面向对象的语言在你有一组固定的$ b $ em> 上的$ b 操作,随着代码的发展,您主要添加新的
事物。这可以通过添加实现
现有方法的新类来实现,并且现有的类是独立的。



功能语言在固定集合
作为您的代码演变的时候,您主要在现有的
事物上添加新的操作。这可以通过添加计算
的新函数与现有的数据类型来完成,而现有的函数是独立的。


假设我有一个动物界面:

  public interface Animal {
public void speak();
}

我有一个 Dog Cat Fish Bird 实现界面。如果我想为 Animal 命名为 jump()添加一个新方法,我将不得不经历所有的我子类和实现 jump()



访问者模式可以缓解这个问题,但是,在Java 8中引入的功能特性,我们应该能够以不同的方式解决这个问题。在 scala 中,我可以很容易地使用模式匹配,但是Java并没有真正拥有它。



8实际上可以更容易地在现有的东西上添加新的操作

解决方案

试图完成,虽然令人钦佩,但在大多数情况下并不适合Java。但在进入之前......

Java 8为接口添加了默认方法!您可以基于界面中的其他方法定义默认方法。这已经可用于抽象类。

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

但最终你会得到为每种类型提供功能。我没有看到添加新方法和创建访问者类或部分函数之间的巨大差异。这只是一个位置问题。你想通过行动或对象来组织你的代码吗? (功能或面向对象,动词或名词等)

我想说的是,Java代码是由'名词'因为原因很快就没有改变。



访客模式和静态方法可能是您按行动组织事物的最佳选择。不过,我认为,当访问者不真正依赖于他们访问的对象的确切类型时,访问者是最有意义的。例如,动物访客可能被用来让动物说话,然后跳跃,因为这些东西都得到所有动物的支持。跳跃访问者对我来说没有多大意义,因为这种行为本身就是每个动物所特有的。

Java使真正的动词方法有点困难,因为它根据参数的编译时间类型选择要运行的重载方法(参见下文和根据参数的实际类型选择重载方法)。方法只能根据 this 的类型动态调度。这是继承是处理这些类型情况的首选方法的原因之一。

  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); //这将调用AnimalActions.jump(Animal)
//因为`a`的类型只是
//编译时的Animal。

你可以通过使用 instanceof 和其他形式的反射。

  public class AnimalActions {
public static void jump(Animal a){
if(鸟的实例){
鸟b =(鸟)a;
// ...
} else if(Cat的一个实例){
Cat c =(Cat)a;
// ...
}
// ...
}
}

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

  Animal a = new Cat(); 
a.jump(); //跳转为一个猫

Java有一些工具可以将一些方法添加到上课更轻松。即抽象类和默认的接口方法。 Java专注于基于调用该方法的对象的调度方法。如果你想编写灵活且高性能的Java,我认为这是你必须采用的一个习惯用法。



PS 因为我是那家伙™我要带上Lisp,特别是Common Lisp Object System(CLOS)。它提供了基于所有参数进行分派的多方法。这本书甚至提供了这是一个与Java不同的例子


This popular answer on Stack Overflow has this to say about the difference between functional programming and object-oriented programming:

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.

Say I have an Animal interface:

public interface Animal {
    public void speak();
}

And I have a Dog, Cat, Fish, and Bird that all implement the interface. If I want to add a new method to Animal named jump(), I would have to go through all of my subclasses and implement jump().

The visitor pattern can alleviate this problem, but it seems that with the new functional features introduced in Java 8 we should be able to solve this problem in a different manner. In scala I could easily just use pattern matching, but Java doesn't really have that yet.

Does Java 8 actually make it any easier to add new operations on existing things?

解决方案

What you're trying to accomplish, while admirable, isn't a good fit for Java in most cases. But before I get into that...

Java 8 adds default methods to interfaces! You can define default methods based on other methods in the interface. This was already available to abstract classes.

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

But in the end, you're going to have to provide functionality for each type. I don't see a huge difference between adding a new method and creating a visitor class or partial functions. It's just a question of location. Do you want to organize your code by action or object? (functional or object oriented, verb or noun, etc.)

I suppose the point I'm trying to make is that Java code is organized by 'noun' for reasons that aren't changing any time soon.

The visitor pattern along with static methods are probably your best bet for organizing things by action. However, I think visitors make the most sense when they don't really depend on the exact type of the object they're visiting. For instance, an Animal visitor might be used to make the animal speak and then jump, because both of those things are supported by all animals. A jump visitor doesn't make as much sense to me because that behavior is inherently specific to each animal.

Java makes a true "verb" approach a little difficult because it chooses which overloaded method to run based on the compile time type of the arguments (see below and Overloaded method selection based on the parameter's real type). Methods are only dynamically dispatched based on the type of this. That's one of the reasons inheritance is the preferred method of handling these types of situations.

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.

You can get around this by using instanceof and other forms of reflection.

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;
            // ...
        }
        // ...
    }
}

But now you're just doing work the JVM was designed to do for you.

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

Java has a few tools that make adding methods to a broad set of classes easier. Namely abstract classes and default interface methods. Java is focused on dispatching methods based on the object invoking the method. If you want to write flexible and performant Java, I think this is one idiom you have to adopt.

P.S. Because I'm That Guy™ I'm going to bring up Lisp, specifically the Common Lisp Object System (CLOS). It provides multimethods that dispatch based on all arguments. The book Practical Common Lisp even provides an example of how this differs from Java.

这篇关于Java 8是否提供了访问者模式的替代方案?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆