使用访客模式而不是强制转换

spr*_*ter 14 java design-patterns

我经常在我的代码中使用访问者模式.当类层次结构实现了访问者时,我将其用作替代instanceof和转换.然而,它导致一些非常尴尬的代码,我想改进.

考虑一下人为的案例:

interface Animal {
    void accept(AnimalVisitor visitor);
}

class Dog implements Animal {
    void accept(AnimalVisitor visitor) {
        visitor.visit(this);
    }
}

class Cat implements Animal {
    void accept(AnimalVisitor visitor) {
        visitor.visit(this);
    }
}

interface AnimalVisitor {
    default void visit(Cat cat) {};
    default void visit(Dog dog) {};
}
Run Code Online (Sandbox Code Playgroud)

在大多数情况下,只做一些特定于狗的事情(例如)我实现了一个在其visit方法中实现逻辑的访问者- 就像模式一样.

但是,有一种情况,我希望从访客那里返回一只可选的狗以便在外面使用.

在这些情况下,我最终得到了一些非常丑陋的代码:

List<Dog> dogs = new ArrayList<>();
animal.accept(new AnimalVisitor() {
    void visit(Dog dog) {
        dogs.add(dog);
    }
}
Optional<Dog> possibleDog = dogs.stream().findAny();
Run Code Online (Sandbox Code Playgroud)

我不能possibleDog直接在访问者内部分配,因为它不是最终变量,因此列表.

这是非常丑陋和低效的,只是为了满足有效终结的要求.我对替代品的想法感兴趣.

我考虑的替代方案:

将访问者转变为可以给予返回值的泛型

interface Animal {
    <T> T accept(AnimalVisitor<T> visitor);
}

interface AnimalVisitor <T> {
    default Optional<T> visit(Dog dog) { return Optional.empty(); }
    default Optional<T> visit(Cat cat) { return Optional.empty(); }
}
Run Code Online (Sandbox Code Playgroud)

创建一个包含大部分代码的抽象访问者,并且可以通过简单的扩展来直接设置可选项

abstract class AnimalCollector implements AnimalVisitor <T> {
    private Optional<T> result = Optional.empty;

    protected void setResult(T value) {
        assert !result.isPresent();
        result = Optional.of(value);
    }

    public Optional<T> asOptional() {
        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

使用流构建器而不是列表

Stream.Builder<Dog> dogs = Stream.builder();
animal.accept(new AnimalVisitor() {
    void visit(Dog dog) {
        dogs.accept(dog);
    }
}
Optional<Dog> possibleDog = dogs.build().findAny();
Run Code Online (Sandbox Code Playgroud)

但我没有发现这些特别优雅.它们涉及很多样板,只是为了实现基本asA逻辑.我倾向于在我的代码中使用第二个解决方案来保持使用清洁.我缺少一个更简单的解决方案吗?

为了清楚起见,我对使用"使用instanceof和强制转换"的变体的答案并不感兴趣.我意识到它可以在这个微不足道的情况下工作,但我正在考虑的情况对访问者的使用相当复杂,包括访问复合材料和代表,这使得投射不切实际.

spr*_*ter 0

我已经尝试过通用访客解决方案。实际上还不错 - 主要的丑陋之处在于Optional<Void>访问者不需要返回值时的使用。

我仍然对任何更好的替代方案感兴趣。

public class Visitor {
    interface Animal {
        <T> Stream<T> accept(AnimalVisitor<T> visitor);
    }

    static class Dog implements Animal {
        @Override
        public String toString() {
            return "Fido";
        }

        @Override
        public <T> Stream<T> accept(AnimalVisitor<T> visitor) {
            return visitor.visit(this).stream();
        }
    }

    static class Cat implements Animal {
        @Override
        public String toString() {
            return "Felix";
        }

        @Override
        public <T> Stream<T> accept(AnimalVisitor<T> visitor) {
            return visitor.visit(this).stream();
        }
    }

    interface AnimalVisitor<T> {
        default Optional<T> visit(Dog dog) {
            return Optional.empty();
        }

        default Optional<T> visit(Cat cat) {
            return Optional.empty();
        }
    }

    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal cat = new Cat();
        AnimalVisitor<Dog> dogFinder = new AnimalVisitor<Dog>() {
            @Override
            public Optional<Dog> visit(Dog dog) {
                return Optional.of(dog);
            }
        };
        AnimalVisitor<Void> dogPrinter = new AnimalVisitor<Void>() {
            @Override
            public Optional<Void> visit(Dog dog) {
                System.out.println("Found dog " + dog);
                return Optional.empty();
            }
        };
        System.out.println(dog.accept(dogFinder).findAny());
        System.out.println(cat.accept(dogFinder).findAny());
        dog.accept(dogPrinter);
        cat.accept(dogPrinter);
    }
}
Run Code Online (Sandbox Code Playgroud)