You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

咨询:访问者模式同时处理两个可访问对象的简洁实现方案

处理二元操作的访问者替代方案

好问题!你碰到的其实是访问者模式在处理二元操作时的天然局限——标准访问者本来就是为单元素访问设计的,直接套用到需要同时处理两个对象的场景,就会出现你说的那种要写一堆重载、逻辑分散的麻烦情况。下面给你几个更简洁的解决方案,按需选择:

1. 改造访问者为「二元访问者」模式

我们可以扩展访问者的思路,设计一个专门处理双元素的二元访问者接口,让元素类配合完成双重分派。这种方式比你原来的方案更集中,避免逻辑散落在各个元素类里。

举个Java的例子:

核心接口与类

// 元素基础接口
interface Element {
    <T> T acceptBinary(BinaryVisitor<T> visitor, Element other);
}

// 数字元素实现
class NumberElement implements Element {
    private int value;
    public NumberElement(int value) { this.value = value; }
    public int getValue() { return value; }

    @Override
    public <T> T acceptBinary(BinaryVisitor<T> visitor, Element other) {
        // 将自己作为左元素,让另一个元素处理右分派
        return other.acceptBinaryRight(visitor, this);
    }

    // 处理自己作为右元素的情况
    public <T> T acceptBinaryRight(BinaryVisitor<T> visitor, NumberElement left) {
        return visitor.visit(left, this);
    }
    public <T> T acceptBinaryRight(BinaryVisitor<T> visitor, StringElement left) {
        return visitor.visit(left, this);
    }
}

// 字符串元素实现
class StringElement implements Element {
    private String value;
    public StringElement(String value) { this.value = value; }
    public String getValue() { return value; }

    @Override
    public <T> T acceptBinary(BinaryVisitor<T> visitor, Element other) {
        return other.acceptBinaryRight(visitor, this);
    }

    public <T> T acceptBinaryRight(BinaryVisitor<T> visitor, NumberElement left) {
        return visitor.visit(left, this);
    }
    public <T> T acceptBinaryRight(BinaryVisitor<T> visitor, StringElement left) {
        return visitor.visit(left, this);
    }
}

// 二元访问者接口
interface BinaryVisitor<T> {
    T visit(NumberElement a, NumberElement b);
    T visit(NumberElement a, StringElement b);
    T visit(StringElement a, NumberElement b);
    T visit(StringElement a, StringElement b);
}

加法访问者实现

class AddVisitor implements BinaryVisitor<Object> {
    @Override
    public Object visit(NumberElement a, NumberElement b) {
        return a.getValue() + b.getValue();
    }

    @Override
    public Object visit(NumberElement a, StringElement b) {
        return String.valueOf(a.getValue()) + b.getValue();
    }

    @Override
    public Object visit(StringElement a, NumberElement b) {
        return a.getValue() + String.valueOf(b.getValue());
    }

    @Override
    public Object visit(StringElement a, StringElement b) {
        return a.getValue() + b.getValue();
    }
}

这种方式把类型组合的逻辑集中在访问者里,元素类只需要处理基础的分派逻辑,比你原来的方案简洁不少。

2. 策略模式+类型注册表(低耦合首选)

如果你的场景里类型或操作会频繁新增,那用策略模式配合类型注册表会更灵活——完全不用修改元素类,新增操作或类型只需要添加新的策略类并注册即可。

实现示例

// 二元操作策略接口
interface BinaryOperationStrategy<T> {
    T execute(Element a, Element b);
}

// 数字加法策略
class NumberAddStrategy implements BinaryOperationStrategy<Integer> {
    @Override
    public Integer execute(Element a, Element b) {
        return ((NumberElement)a).getValue() + ((NumberElement)b).getValue();
    }
}

// 数字+字符串拼接策略
class NumberStringAddStrategy implements BinaryOperationStrategy<String> {
    @Override
    public String execute(Element a, Element b) {
        return String.valueOf(((NumberElement)a).getValue()) + ((StringElement)b).getValue();
    }
}

// 策略注册表
class OperationRegistry {
    private static final Map<ClassPair, BinaryOperationStrategy<?>> STRATEGIES = new HashMap<>();

    // 静态注册所有支持的操作组合
    static {
        STRATEGIES.put(new ClassPair(NumberElement.class, NumberElement.class), new NumberAddStrategy());
        STRATEGIES.put(new ClassPair(NumberElement.class, StringElement.class), new NumberStringAddStrategy());
        // 注册其他组合...
    }

    @SuppressWarnings("unchecked")
    public static <T> T execute(Element a, Element b) {
        ClassPair key = new ClassPair(a.getClass(), b.getClass());
        BinaryOperationStrategy<T> strategy = (BinaryOperationStrategy<T>) STRATEGIES.get(key);
        if (strategy == null) {
            throw new UnsupportedOperationException("不支持的类型组合: " + a.getClass() + " + " + b.getClass());
        }
        return strategy.execute(a, b);
    }

    // 辅助类,用作Map的键
    private static class ClassPair {
        private final Class<?> leftType;
        private final Class<?> rightType;

        public ClassPair(Class<?> leftType, Class<?> rightType) {
            this.leftType = leftType;
            this.rightType = rightType;
        }

        // 必须重写equals和hashCode才能作为Map键
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            ClassPair classPair = (ClassPair) o;
            return leftType.equals(classPair.leftType) && rightType.equals(classPair.rightType);
        }

        @Override
        public int hashCode() {
            return Objects.hash(leftType, rightType);
        }
    }
}

使用时直接调用OperationRegistry.execute(a, b)即可,完全解耦了元素和操作逻辑。

3. 利用现代语言的模式匹配(最简洁)

如果你的语言支持模式匹配(比如Java 17+、C#、Kotlin、Scala等),这可能是最简洁的方案——不用任何额外的接口或类,直接通过类型匹配处理不同组合:

Java 17+ 示例

public static Object add(Element a, Element b) {
    return switch ((a, b)) {
        case (NumberElement na, NumberElement nb) -> na.getValue() + nb.getValue();
        case (NumberElement na, StringElement sb) -> String.valueOf(na.getValue()) + sb.getValue();
        case (StringElement sa, NumberElement nb) -> sa.getValue() + String.valueOf(nb.getValue());
        case (StringElement sa, StringElement sb) -> sa.getValue() + sb.getValue();
        default -> throw new UnsupportedOperationException("不支持的操作");
    };
}

这种方式代码直观易懂,适合类型和操作都相对固定的场景,几乎没有冗余代码。

总结

  • 如果类型和操作都较少,二元访问者模式足够简洁且符合访问者的设计思想;
  • 如果类型或操作频繁变动,策略+注册表模式是低耦合的首选;
  • 如果用的是现代语言,模式匹配是最直接的解决方案。

内容的提问来源于stack exchange,提问作者Eliott

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:31:49