使用访问者模式替代类型转换:优化冗余代码方案问询
优化访问者模式的返回值获取,减少样板代码
我太理解这种困扰了——用访问者模式本来是为了避开instanceof和强制类型转换的坏味道,结果为了获取一个简单的返回值,却要写一堆绕来绕去的样板代码,完全违背了使用这个模式的初衷。针对你的需求,我整理了几种更简洁的优化方案,既能保持访问者模式的类型安全,又能大幅减少冗余代码:
方案1:封装泛型访问者+静态工厂方法
首先,我们把访问者接口改成支持返回Optional<T>的泛型接口,然后在工具类里封装好常用的类型收集逻辑,避免每次都写匿名内部类:
核心代码
// 基础Animal接口,支持返回泛型Optional interface Animal { <T> Optional<T> accept(AnimalVisitor<T> visitor); } // 泛型访问者接口,默认方法返回empty,只需要实现关心的类型 interface AnimalVisitor<T> { default Optional<T> visit(Dog dog) { return Optional.empty(); } default Optional<T> visit(Cat cat) { return Optional.empty(); } } // Dog和Cat的实现和之前一致,只是返回Optional class Dog implements Animal { @Override public <T> Optional<T> accept(AnimalVisitor<T> visitor) { return visitor.visit(this); } } class Cat implements Animal { @Override public <T> Optional<T> accept(AnimalVisitor<T> visitor) { return visitor.visit(this); } } // 工具类:封装常用的类型收集访问者 class AnimalVisitors { // 获取Dog的访问者 public static AnimalVisitor<Dog> collectDog() { return new AnimalVisitor<>() { @Override public Optional<Dog> visit(Dog dog) { return Optional.of(dog); } }; } // 获取Cat的访问者 public static AnimalVisitor<Cat> collectCat() { return new AnimalVisitor<>() { @Override public Optional<Cat> visit(Cat cat) { return Optional.of(cat); } }; } }
使用方式
Animal animal = new Dog(); // 一行代码获取Optional<Dog> Optional<Dog> possibleDog = animal.accept(AnimalVisitors.collectDog());
这个方案的优势是外部调用极其简洁,所有样板代码都被封装在工具类里,新增类型时只需要在工具类里加一个静态方法即可。
方案2:通用类型收集访问者
如果你的Animal子类较多,不想为每个类型都写静态方法,可以实现一个通用的类型收集器,内部通过类型检查完成转换(但外部代码完全看不到instanceof或强制转换):
核心代码
class GenericTypeCollector<T> implements AnimalVisitor<T> { private final Class<T> targetType; public GenericTypeCollector(Class<T> targetType) { this.targetType = targetType; } @SuppressWarnings("unchecked") @Override public Optional<T> visit(Dog dog) { return targetType.isInstance(dog) ? Optional.of((T) dog) : Optional.empty(); } @SuppressWarnings("unchecked") @Override public Optional<T> visit(Cat cat) { return targetType.isInstance(cat) ? Optional.of((T) cat) : Optional.empty(); } }
使用方式
Animal animal = new Cat(); // 传入目标类型即可获取Optional Optional<Cat> possibleCat = animal.accept(new GenericTypeCollector<>(Cat.class));
这个方案的优势是扩展性极强,新增Animal子类时,只需要在GenericTypeCollector里新增对应的visit方法即可,外部调用不需要修改。
方案3:优化你现有的抽象Collector
你目前使用的抽象AnimalCollector思路很好,我们可以给它加上静态工厂方法,进一步简化使用:
核心代码
abstract class AnimalCollector<T> implements AnimalVisitor { private Optional<T> result = Optional.empty(); protected void setResult(T value) { if (result.isPresent()) { throw new IllegalStateException("Result can only be set once"); } result = Optional.of(value); } public Optional<T> getResult() { return result; } // 静态工厂:创建Dog收集器 public static AnimalCollector<Dog> dogCollector() { return new AnimalCollector<>() { @Override public void visit(Dog dog) { setResult(dog); } }; } }
使用方式
AnimalCollector<Dog> collector = AnimalCollector.dogCollector(); animal.accept(collector); Optional<Dog> possibleDog = collector.getResult();
这个方案保留了你熟悉的使用习惯,同时通过静态工厂减少了重复编写匿名内部类的工作量。
总结
如果你的业务场景中类型较少,方案1是最简洁的;如果类型较多且经常新增,方案2的扩展性更好;如果不想改动现有代码结构,方案3是对你现有实现的最小优化。三种方案都能彻底摆脱之前用列表/Stream.Builder绕圈的冗余写法,同时保持访问者模式的类型安全优势。
内容的提问来源于stack exchange,提问作者sprinter
相关产品推荐
相关产品推荐

