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和强制转换"的变体的答案并不感兴趣.我意识到它可以在这个微不足道的情况下工作,但我正在考虑的情况对访问者的使用相当复杂,包括访问复合材料和代表,这使得投射不切实际.
我已经尝试过通用访客解决方案。实际上还不错 - 主要的丑陋之处在于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)