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

Java树形结构设计疑问:如何优雅处理多类型Node的属性访问?

嘿,这个问题其实正好是访问者模式的典型应用场景——你已经选对了方向,只是还没把访问者模式的骨架搭起来而已。空接口Node没法提供多态的行为入口,我们需要给它加一个核心方法来支持访问者,同时完全避免instanceof和强制转换,还能轻松扩展新的节点类型。

核心思路:用访问者模式重构,让节点主动接受访问

首先要给Node接口添加一个accept方法,作为访问者的入口;然后定义一个NodeVisitor接口,针对每个具体节点类型提供对应的处理方法。这样每个具体节点类(A、B,未来的C、D)只需要实现accept,把自己交给访问者处理,上层代码完全不用判断类型。

步骤1:重构Node接口和定义Visitor

// 重构后的Node接口,添加accept方法作为访问者入口
interface Node {
    void accept(NodeVisitor visitor);
}

// 访问者接口,每个具体节点对应一个专属的visit方法
interface NodeVisitor {
    void visit(A node);
    void visit(B node);
    // 未来新增节点类型时,只需在这里补充对应的visit方法即可
}

步骤2:修改A、B类实现accept方法

让每个具体节点主动调用访问者的对应方法,把自身传递过去:

class A implements Node {
    public String a;
    public A(String a){ this.a = a; }

    @Override
    public void accept(NodeVisitor visitor) {
        visitor.visit(this); // 把当前A实例交给访问者处理
    }
}

class B implements Node {
    public int a = 5;
    public String z = "xyz";
    public B(int a,String z){ this.a = a; this.z = z; }

    @Override
    public void accept(NodeVisitor visitor) {
        visitor.visit(this); // 把当前B实例交给访问者处理
    }
}

步骤3:完善Parse类的实例创建逻辑

给parseA和parseB补充具体的参数生成逻辑(这里用示例值,你可以根据业务需求调整参数来源):

import java.util.List;

class Parse {
    List<Boolean> l;
    private static int i=0;
    Parse(List<Boolean> l){ this.l = l; }
    private Node parseA() { 
        // 示例:返回一个带具体参数的A实例
        return new A("来自Parse的A节点"); 
    }
    private Node parseB() { 
        // 示例:返回一个带自定义参数的B实例
        return new B(15, "Parse生成的z值"); 
    }
    private boolean getNextState(){ return l.get(i++); }
    public Node parse(){
        boolean x = getNextState();
        if(x){ return parseA(); } else{ return parseB(); }
    }
}

步骤4:在Test类中用访问者处理节点属性

现在你可以创建具体的访问者实现,针对性处理不同节点的属性,全程不需要instanceof:

import java.util.Arrays;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        List<Boolean> l = Arrays.asList(true,false);
        Parse p = new Parse(l);
        
        // 处理第一个节点(A类型)
        Node node1 = p.parse();
        node1.accept(new NodeVisitor() {
            @Override
            public void visit(A node) {
                System.out.println("处理A节点:属性a = " + node.a);
            }

            @Override
            public void visit(B node) {
                // 该方法不会被调用,因为node1是A类型
            }
        });
        
        // 处理第二个节点(B类型)
        Node node2 = p.parse();
        node2.accept(new NodeVisitor() {
            @Override
            public void visit(A node) {
                // 该方法不会被调用,因为node2是B类型
            }

            @Override
            public void visit(B node) {
                System.out.println("处理B节点:属性a = " + node.a + ",属性z = " + node.z);
            }
        });

        // 如果需要复用访问逻辑,可以单独实现一个访问者类
        NodeVisitor printVisitor = new NodeVisitor() {
            @Override
            public void visit(A node) {
                System.out.println("[复用访问者] A节点的a值:" + node.a);
            }

            @Override
            public void visit(B node) {
                System.out.println("[复用访问者] B节点的a值:" + node.a + ",z值:" + node.z);
            }
        };
        // 测试复用逻辑
        Parse p2 = new Parse(Arrays.asList(true));
        Node node3 = p2.parse();
        node3.accept(printVisitor);
    }
}

扩展性说明

如果未来需要新增节点类型C,只需要完成3件事:

  1. 新建C implements Node类,实现accept方法
  2. 在NodeVisitor接口中添加void visit(C node);
  3. 编写对应的访问者实现(如果需要处理C的属性)

完全不需要修改现有的A、B、Parse类或已有的访问者逻辑,完美符合开闭原则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 20:32:27