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件事:
- 新建
C implements Node类,实现accept方法 - 在
NodeVisitor接口中添加void visit(C node); - 编写对应的访问者实现(如果需要处理C的属性)
完全不需要修改现有的A、B、Parse类或已有的访问者逻辑,完美符合开闭原则。
内容的提问来源于stack exchange,提问作者Dhyey Shah
相关产品推荐
相关产品推荐

