Java 什么';添加有关现有对象的附加信息的更好方法是什么?

Java 什么';添加有关现有对象的附加信息的更好方法是什么?,java,Java,例如,我有一个用于二叉树的节点类 public class Node { public Node lchild; public Node rchild; // public for convenience } 现在,我有一个处理器,它需要记录有关节点实例的一些附加信息,并且只能私下使用它们。让我们假设预排序树遍历中的数字 我认为一个直接的方法是在类中添加一个字段: public class Node { public Node lchild; public No

例如,我有一个用于二叉树的
节点

public class Node {
    public Node lchild;
    public Node rchild; // public for convenience
}
现在,我有一个处理器,它需要记录有关
节点
实例的一些附加信息,并且只能私下使用它们。让我们假设预排序树遍历中的数字

我认为一个直接的方法是在类中添加一个字段:

public class Node {
    public Node lchild;
    public Node rchild;
    public int no;   // the number in the pre-order tree traverse
}
不过,我相信这绝对是个坏主意。所以我现在使用的是:使用
Map

公共类MyProcessor{
私人地图编号;
公共无效进程1(节点){
int id=no.get(node);//或类似的内容
}
}
当然,这可以解决问题。但我担心的是:

  • 频繁访问地图似乎效率较低?(与添加字段方法相比)
  • 如果我需要更多种类的信息,我需要制作更多的地图,这似乎是一场噩梦

  • 那么,有没有更好的办法?谢谢

    最好的解决方案可能是扩展您拥有的
    节点
    对象,这将允许您在不破坏现有代码的同时利用新功能


    您还可以将其与和模式结合起来,尝试创建一个透明、结构化的对象创建过程。

    这是一个关注点分离的问题。是要添加树的节点或处理器关注点的信息

    我想说,下面的陈述描述了这种情况

    • 树具有节点(合成聚合)
    • 该节点具有多个节点(聚合)
    • 该节点具有节点信息(聚合)
    • 处理器处理树(依赖项)
    因此,我将把信息与节点放在一起或关联起来。为了具有一定程度的灵活性,您可以获得节点扩展的信息

    class ExtNode extends Node {
    
        int no;
    
    }
    
    或装饰师:

    class NodeDecorator {
    
        Node node;
        int no;
    
        NodeDecorator(node){
            this.node = node;
        }
    }
    
    如果你选择地图,在这个阶段,效率不应该是你关心的问题。访问地图是相当有效的。如果需要,您可以稍后进行优化

    如果添加了其他信息,您可以定义一个新的结构,比如说
    NodeInfo
    ,将所有信息放在其中,并将其放在映射中或节点上,而不是int

    比如说

    class NodeInfo {
        int no;
        String other;
    }
    

    附加信息特定于节点层次结构的一个处理器,因此不应泄漏到节点中(关注点分离)

    可能的解决办法:

  • 为此处理器使用并行树结构
  • 由于节点是树结构,因此使用在访问节点结构时构建的并行结构。此并行项(ProcessedNode?)将保存附加信息、对节点的引用以及左/右ProcessedNode子项

    这在内存和访问方面可能更好(没有映射开销),但会使处理器更复杂:它将访问ProcessedNode而不是Node。它不会访问下一个节点,而是为下一个节点创建ProcessedNode,并访问ProcessedNode

    您可以延迟构建ProcessedNode,这样就可以根据需要构建并行树

    如果节点树可能发生变异,并且需要将ProcessedNode信息保留超过一次处理的持续时间,这是不实际的

  • 使用地图
  • 您可以在包含附加信息的类详细信息中收集所有这些信息,并使用单个映射,而不是每个附加信息都有一个映射

    此解决方案的优点是实现简单。 缺点是,如果保留贴图,则需要在节点消失时进行维护

    最终的选择取决于需要多长时间的附加详细信息,如果您只需要在处理期间维护信息,则在处理结束时会删除映射,因此不会利用向映射添加项目的成本


    如果创建一个映射是可行的,那么在使用替代解决方案进行优化之前,您应该测量实际开销:该收益值得付出努力吗?

    对于子分类方法,您应该使用“getter方法”访问子节点,以便子类可以覆盖它们

    public class Node {
        protected Node left;
        protected Node right;
    
        public Node(Node left, Node right) {
            this.left = left;
            this.right = right;
        }
    
        public Node getLeft() { return left; }
        public Node getRight() { return right; }
    }
    
    并在子类中重写这些,在需要时惰性地创建子节点

    public class DecoratedNode extends Node {
        private Node original;
        private int no = 0;
    
        public DecoratedNode(Node n) {
            super(null, null);
            original = n;
        }
    
        @Override
        public Node getLeft() {
            if (left == null && original.getLeft() != null) 
                left = new DecoratedNode(original.getLeft());
            return left; 
        }
    
        @Override
        public Node getRight() { 
            if (right == null && original.getRight() != null) 
                right = new DecoratedNode(original.getRight());
            return right; 
        }
    
        public int getNo() { return no; }
        public void setNo(int no) { this.no = no; }
    }
    

    然后将新的DecoratedNode(root)传递给进程方法。

    “但是,我认为这绝对是个坏主意。”为什么?
    Node
    是否在其他处理器中重复使用?@T.J.Crowder是的,
    Node
    被广泛共享。我想你可以将
    Node
    子类化,并添加你想要的任意多的信息。@devuth是的,这是一个解决方案。但是我似乎需要复制
    节点
    实例已经拥有的所有信息。您觉得使用第二种解决方案会影响性能吗?我想不会。否则,您可能会将其子类化为ProcesserNode,但如果在整个应用程序中共享相同的引用,则需要大量装箱和拆箱(性能较差)。
    public class DecoratedNode extends Node {
        private Node original;
        private int no = 0;
    
        public DecoratedNode(Node n) {
            super(null, null);
            original = n;
        }
    
        @Override
        public Node getLeft() {
            if (left == null && original.getLeft() != null) 
                left = new DecoratedNode(original.getLeft());
            return left; 
        }
    
        @Override
        public Node getRight() { 
            if (right == null && original.getRight() != null) 
                right = new DecoratedNode(original.getRight());
            return right; 
        }
    
        public int getNo() { return no; }
        public void setNo(int no) { this.no = no; }
    }